/* linphone_jni.cc Copyright (C) 2017 Belledonne Communications SARL This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "belle-sip/object.h" #include "mediastreamer2/mediastream.h" #include "mediastreamer2/mscommon.h" #include "mediastreamer2/msmediaplayer.h" #include "mediastreamer2/msutils.h" #include "mediastreamer2/devices.h" #include "mediastreamer2/msjava.h" #include "linphone/core_utils.h" #include "linphone/core.h" #include "linphone/tunnel.h" #include "linphone/account_creator.h" #include "linphone/wrapper_utils.h" #include "linphone/lpconfig.h" #ifdef __ANDROID__ #include #include #endif /* __ANDROID__ */ static JavaVM *jvm = NULL; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *ajvm, void *reserved) { #ifdef __ANDROID__ ms_set_jvm(ajvm); #endif /* __ANDROID__ */ jvm = ajvm; return JNI_VERSION_1_2; } #define belle_sip_java_user_data_key "java_object" static const char* GetStringUTFChars(JNIEnv* env, jstring string) { const char *cstring = string ? env->GetStringUTFChars(string, NULL) : NULL; return cstring; } static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char *cstring) { if (string) env->ReleaseStringUTFChars(string, cstring); } static jlong GetObjectNativePtr(JNIEnv *env, jobject object) { jclass objClass = env->GetObjectClass(object); jfieldID nativePtrId = env->GetFieldID(objClass, "nativePtr", "J"); jlong nativePtr = env->GetLongField(object, nativePtrId); return nativePtr; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class LinphoneJavaBindings { public: LinphoneJavaBindings(JNIEnv *env) { {{#objects}} {{cPrefix}}_class = (jclass)env->NewGlobalRef(env->FindClass("{{jniPath}}{{classImplName}}")); {{cPrefix}}_class_constructor = env->GetMethodID({{cPrefix}}_class, "", "(J)V"); {{/objects}} {{#enums}} {{cPrefix}}_class = (jclass)env->NewGlobalRef(env->FindClass("{{jniPath}}{{jniName}}")); {{cPrefix}}_class_constructor_from_int = env->GetStaticMethodID({{cPrefix}}_class, "fromInt", "(I)L{{jniPath}}{{jniName}};"); {{/enums}} } ~LinphoneJavaBindings() { JNIEnv *env = 0; jvm->AttachCurrentThread(&env,NULL); {{#objects}} env->DeleteGlobalRef({{cPrefix}}_class); {{/objects}} {{#enums}} env->DeleteGlobalRef({{cPrefix}}_class); {{/enums}} } {{#objects}} jclass {{cPrefix}}_class; jmethodID {{cPrefix}}_class_constructor; {{/objects}} {{#enums}} jclass {{cPrefix}}_class; jmethodID {{cPrefix}}_class_constructor_from_int; {{/enums}} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {{#objects}} jobject get{{className}}(JNIEnv *env, {{classCName}} *cptr) { jobject jobj = 0; if (cptr != NULL) { void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key); LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get()); if (!ljb) { ljb = new LinphoneJavaBindings(env); linphone_factory_set_user_data(linphone_factory_get(), ljb); } jclass {{cPrefix}}_class = ljb->{{cPrefix}}_class; jmethodID {{cPrefix}}_constructor = ljb->{{cPrefix}}_class_constructor; if (up == NULL) { jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr); belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), NULL); {{cPrefix}}_ref(cptr); } else { jobj = env->NewLocalRef((jobject)up); if (jobj == NULL) { // Delete weak ref ? env->DeleteWeakGlobalRef((jobject)up); // takes implicit local ref jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr); belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), NULL); {{cPrefix}}_ref(cptr); } } } return jobj; } void Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong ptr) { {{classCName}} *cptr = ({{classCName}}*)ptr; jobject wref = (jobject)belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key); belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, NULL, NULL); if (wref) { env->DeleteWeakGlobalRef(wref); } {{cPrefix}}_unref(cptr); } {{/objects}} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static inline void handle_possible_java_exception(JNIEnv *env, jobject listener) { if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); ms_error("Listener %p raised an exception",listener); } } {{#callbacks}} static {{return}} {{callbackName}}({{params}}) { JNIEnv *env = 0; jint jvmResult = jvm->AttachCurrentThread(&env,NULL); if (jvmResult != 0) { ms_error("cannot attach VM"); return{{returnIfFail}}; } {{#isSingleListener}} {{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks({{firstParam}}); {{/isSingleListener}} {{#isMultiListener}} {{classCName}}Cbs *cbs = {{cPrefix}}_get_current_callbacks({{firstParam}}); {{/isMultiListener}} jobject jlistener = (jobject) {{cPrefix}}_cbs_get_user_data(cbs); if (jlistener == NULL) { ms_warning("{{name}}() notification without listener"); return{{returnIfFail}}; } jclass jlistenerClass = (jclass) env->GetObjectClass(jlistener); jmethodID jcallback = env->GetMethodID(jlistenerClass, "{{jname}}", "{{jparams}}"); env->DeleteLocalRef(jlistenerClass); {{#jobjects}} jobject j_{{objectName}} = get{{className}}(env, (Linphone{{className}} *){{objectName}}); {{/jobjects}} {{#jenums}} LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get()); jobject j_{{enumName}} = env->CallStaticObjectMethod(ljb->{{cEnumPrefix}}_class, ljb->{{cEnumPrefix}}_class_constructor_from_int, (jint){{enumName}}); {{/jenums}} {{#jstrings}} jstring j_{{stringName}} = {{stringName}} ? env->NewStringUTF({{stringName}}) : NULL; {{/jstrings}} env->CallVoidMethod(jlistener, jcallback, {{params_impl}}); {{#jobjects}} if (j_{{objectName}}) { env->DeleteLocalRef(j_{{objectName}}); } {{/jobjects}} {{#jstrings}} if (j_{{stringName}}) { env->DeleteLocalRef(j_{{stringName}}); } {{/jstrings}} handle_possible_java_exception(env, jlistener); {{#hasReturn}} return 0; {{/hasReturn}} } {{/callbacks}} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {{#interfaces}} {{#isSingleListener}} void {{jniPackage}}{{classCName}}Impl_setListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) { {{/isSingleListener}} {{#isMultiListener}} void {{jniPackage}}{{classCName}}Impl_addListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) { {{/isMultiListener}} {{classCName}} *cptr = ({{classCName}}*)ptr; jobject listener = env->NewGlobalRef(jlistener); {{#isSingleListener}} {{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks(cptr); {{/isSingleListener}} {{#isMultiListener}} {{classCName}}Cbs *cbs = linphone_factory_create_{{factoryName}}_cbs(NULL); {{/isMultiListener}} {{cPrefix}}_cbs_set_user_data(cbs, listener); {{#callbacksList}} {{cPrefix}}_cbs_set_{{callback}}(cbs, {{callbackName}}); {{/callbacksList}} {{#isMultiListener}} {{cPrefix}}_add_callbacks(cptr, cbs); {{/isMultiListener}} } {{#isMultiListener}} void {{jniPackage}}{{classCName}}Impl_removeListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) { {{classCName}} *cptr = ({{classCName}}*)ptr; const bctbx_list_t *cbs_list = {{cPrefix}}_get_callbacks_list(cptr); bctbx_list_t *it; for (it = (bctbx_list_t *)cbs_list; it != NULL; it = it->next) { {{classCName}}Cbs *cbs = ({{classCName}}Cbs *)it->data; if ({{cPrefix}}_cbs_get_user_data(cbs) == jlistener) { {{cPrefix}}_remove_callbacks(cptr, cbs); break; } } } {{/isMultiListener}} {{/interfaces}} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {{#methods}} {{return}} {{name}}({{params}}) { {{#notStatic}}{{classCName}} *cptr = ({{classCName}}*)ptr;{{/notStatic}}{{#strings}} const char* c_{{string}} = GetStringUTFChars(env, {{string}}); {{/strings}}{{#objects}} {{objectClassCName}}* c_{{object}} = NULL; if ({{object}}) c_{{object}} = ({{objectClassCName}}*)GetObjectNativePtr(env, {{object}}); {{/objects}}{{#lists}} bctbx_list_t *bctbx_list_{{list}} = NULL; int {{list}}_count = env->GetArrayLength({{list}}); for (int i=0; i < {{list}}_count; i++) { {{#isStringList}} jstring obj = (jstring) env->GetObjectArrayElement({{list}}, i); const char *str = GetStringUTFChars(env, obj); if (str) { bctbx_list_{{list}} = bctbx_list_append(bctbx_list_{{list}}, ms_strdup(str)); ReleaseStringUTFChars(env, obj, str); } {{/isStringList}} {{#isObjList}} jobject obj = env->GetObjectArrayElement({{list}}, i); bctbx_list_{{list}} = bctbx_list_append(bctbx_list_{{list}}, ({{objectClassCName}} *)GetObjectNativePtr(env, obj)); {{/isObjList}} } {{/lists}}{{#hasListReturn}} const bctbx_list_t *list = {{c_name}}({{#notStatic}}cptr{{/notStatic}}{{params_impl}}); size_t count = bctbx_list_size(list); {{#isRealObjectArray}} LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get()); jobjectArray jni_list_result = env->NewObjectArray((int)count, ljb->{{objectCPrefix}}_class, NULL);{{/isRealObjectArray}} {{#isStringObjectArray}}jobjectArray jni_list_result = env->NewObjectArray((int)count, env->FindClass("java/lang/String"), env->NewStringUTF(""));{{/isStringObjectArray}} for (size_t i = 0; i < count; i++) { {{#isRealObjectArray}} {{objectClassCName}}* c_object = ({{objectClassCName}}*)list->data; jobject object = get{{objectClassName}}(env, c_object); {{/isRealObjectArray}} {{#isStringObjectArray}}const char *cstring = (const char *)list->data; jstring object = cstring ? env->NewStringUTF(cstring) : 0;{{/isStringObjectArray}} if (object != 0) { env->SetObjectArrayElement(jni_list_result, (int)i, object); {{#isRealObjectArray}}env->DeleteLocalRef(object);{{/isRealObjectArray}} } list = bctbx_list_next(list); } {{/hasListReturn}}{{#hasStringReturn}} const char *c_string = {{c_name}}({{#notStatic}}cptr{{/notStatic}}{{params_impl}}){{#returnObject}}){{/returnObject}}; jstring jni_result = (c_string != NULL) ? env->NewStringUTF(c_string) : NULL; {{/hasStringReturn}}{{#hasNormalReturn}} {{#hasReturn}}{{return}} jni_result = ({{return}}){{#returnObject}}get{{returnClassName}}(env, (Linphone{{returnClassName}} *){{/returnObject}}{{/hasReturn}}{{c_name}}({{#notStatic}}cptr{{/notStatic}}{{params_impl}}){{#returnObject}}){{/returnObject}}; {{/hasNormalReturn}}{{#strings}} ReleaseStringUTFChars(env, {{string}}, c_{{string}}); {{/strings}}{{#hasReturn}}return jni_result;{{/hasReturn}}{{#hasListReturn}}return jni_list_result;{{/hasListReturn}} } {{/methods}}