jni.mustache 15.7 KB
Newer Older
Sylvain Berfini's avatar
Sylvain Berfini committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
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 <jni.h>
#include <cpu-features.h>

23
#include "belle-sip/object.h"
Sylvain Berfini's avatar
Sylvain Berfini committed
24 25 26 27 28 29
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/mscommon.h"
#include "mediastreamer2/msmediaplayer.h"
#include "mediastreamer2/msutils.h"
#include "mediastreamer2/devices.h"
#include "mediastreamer2/msjava.h"
30
#include "linphone/core_utils.h"
Sylvain Berfini's avatar
Sylvain Berfini committed
31 32 33 34 35 36 37 38 39 40 41
#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 <android/log.h>
#include <belle-sip/wakelock.h>
#endif /* __ANDROID__ */

42
static JavaVM *jvm = NULL;
Sylvain Berfini's avatar
Sylvain Berfini committed
43 44 45
static const char* LogDomain = "Linphone";
static jmethodID loghandler_id;
static jobject handler_obj=NULL;
Sylvain Berfini's avatar
Sylvain Berfini committed
46

Sylvain Berfini's avatar
Sylvain Berfini committed
47 48 49 50
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *ajvm, void *reserved) {
#ifdef __ANDROID__
	ms_set_jvm(ajvm);
#endif /* __ANDROID__ */
51
	jvm = ajvm;
Sylvain Berfini's avatar
Sylvain Berfini committed
52 53 54
	return JNI_VERSION_1_2;
}

55 56
#define belle_sip_java_user_data_key "java_object"

Sylvain Berfini's avatar
Sylvain Berfini committed
57 58 59 60 61 62 63
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);
64 65 66 67 68 69 70 71 72
}

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;
}

73 74
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
void linphone_android_log_handler(int prio, char *str) {
	char *current;
	char *next;

	if (strlen(str) < 512) {
		__android_log_write(prio, LogDomain, str);
	} else {
		current = str;
		while ((next = strchr(current, '\n')) != NULL) {

			*next = '\0';
			if (next != str && next[-1] == '\r')
				next[-1] = '\0';
			__android_log_write(prio, LogDomain, current);
			current = next + 1;
		}
		__android_log_write(prio, LogDomain, current);
	}
}

static void linphone_android_ortp_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args) {
	char* str = bctbx_strdup_vprintf(fmt, args);
    const char *levname = "undef";

    if (str == NULL) return;

    int prio;
    switch(lev) {
    	case ORTP_DEBUG:	prio = ANDROID_LOG_DEBUG;	levname="debug"; break;
    	case ORTP_MESSAGE:	prio = ANDROID_LOG_INFO;	levname="message"; break;
    	case ORTP_WARNING:	prio = ANDROID_LOG_WARN;	levname="warning"; break;
    	case ORTP_ERROR:	prio = ANDROID_LOG_ERROR;	levname="error"; break;
    	case ORTP_FATAL:	prio = ANDROID_LOG_FATAL;	levname="fatal"; break;
    	default:			prio = ANDROID_LOG_DEFAULT;	break;
    }

    if (handler_obj) {
    	JNIEnv *env = ms_get_jni_env();
    	jstring jdomain = env->NewStringUTF(LogDomain);
    	jstring jlevname = env->NewStringUTF(levname);
    	jstring jstr = env->NewStringUTF(str);
    	env->CallVoidMethod(handler_obj, loghandler_id, jdomain, (jint)lev, jlevname, jstr, NULL);
    	if (jdomain) env->DeleteLocalRef(jdomain);
    	if (jlevname) env->DeleteLocalRef(jlevname);
    	if (jstr) env->DeleteLocalRef(jstr);
    } else {
    	linphone_android_log_handler(prio, str);
    }
    bctbx_free(str);
}

126
extern "C" void Java_org_linphone_core_FactoryImpl_setDebugMode(JNIEnv* env, jobject thiz, jboolean isDebug, jstring jdebugTag) {
127 128 129 130 131 132 133 134 135 136
	if (isDebug) {
		LogDomain = GetStringUTFChars(env, jdebugTag);
		linphone_core_enable_logs_with_cb(linphone_android_ortp_log_handler);
	} else {
		linphone_core_disable_logs();
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

137 138 139
class LinphoneJavaBindings {
public:
	LinphoneJavaBindings(JNIEnv *env) {
140 141 142
		ms_factory_class = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/mediastream/Factory"));
		ms_factory_class_constructor = env->GetMethodID(ms_factory_class, "<init>", "(J)V");

143 144 145 146
		{{#objects}}
		{{cPrefix}}_class = (jclass)env->NewGlobalRef(env->FindClass("{{jniPath}}{{classImplName}}"));
		{{cPrefix}}_class_constructor = env->GetMethodID({{cPrefix}}_class, "<init>", "(J)V");
		{{/objects}}
147 148 149 150 151

		{{#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}}
152 153 154 155 156
	}

	~LinphoneJavaBindings() {
		JNIEnv *env = 0;
		jvm->AttachCurrentThread(&env,NULL);
157 158 159

		env->DeleteGlobalRef(ms_factory_class);

160 161 162
		{{#objects}}
		env->DeleteGlobalRef({{cPrefix}}_class);
		{{/objects}}
163

164 165 166
		{{#enums}}
		env->DeleteGlobalRef({{cPrefix}}_class);
		{{/enums}}
167 168
	}

169 170 171
	jclass ms_factory_class;
	jmethodID ms_factory_class_constructor;

172 173 174 175
	{{#objects}}
	jclass {{cPrefix}}_class;
	jmethodID {{cPrefix}}_class_constructor;
	{{/objects}}
176 177 178 179 180

	{{#enums}}
	jclass {{cPrefix}}_class;
	jmethodID {{cPrefix}}_class_constructor_from_int;
	{{/enums}}
181 182
};

183 184
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Sylvain Berfini's avatar
Sylvain Berfini committed
185
{{#objects}}
186
jobject get{{className}}(JNIEnv *env, {{classCName}} *cptr) {
Sylvain Berfini's avatar
Sylvain Berfini committed
187 188
	jobject jobj = 0;

189 190
	if (cptr != NULL) {
		void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);
191
		LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
192 193 194 195 196
		if (!ljb) {
			ljb = new LinphoneJavaBindings(env);
			linphone_factory_set_user_data(linphone_factory_get(), ljb);
		}

197 198
		jclass {{cPrefix}}_class = ljb->{{cPrefix}}_class;
		jmethodID {{cPrefix}}_constructor = ljb->{{cPrefix}}_class_constructor;
Sylvain Berfini's avatar
Sylvain Berfini committed
199 200

		if (up == NULL) {
201 202 203
			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);
Sylvain Berfini's avatar
Sylvain Berfini committed
204 205 206 207 208 209
		} else {
			jobj = env->NewLocalRef((jobject)up);
			if (jobj == NULL) {
				// Delete weak ref ?
				env->DeleteWeakGlobalRef((jobject)up);
				// takes implicit local ref
210 211 212
				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);
Sylvain Berfini's avatar
Sylvain Berfini committed
213 214 215 216 217 218 219 220
			}
		}
	}
	return jobj;
}

void Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong ptr) {
	{{classCName}} *cptr = ({{classCName}}*)ptr;
221 222
	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);
Sylvain Berfini's avatar
Sylvain Berfini committed
223 224 225 226 227 228 229
	if (wref) {
		env->DeleteWeakGlobalRef(wref);
	}
	{{cPrefix}}_unref(cptr);
}

{{/objects}}
230 231 232 233 234 235 236 237 238 239 240
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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);
	}
}

241
{{#callbacks}}
242 243
static {{return}} {{callbackName}}({{params}}) {
	JNIEnv *env = 0;
244 245
	jint jvmResult = jvm->AttachCurrentThread(&env,NULL);
	if (jvmResult != 0) {
246
		ms_error("cannot attach VM");
247
		return{{returnIfFail}};
248 249
	}

250
	{{#isSingleListener}}
251
	{{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks({{firstParam}});
252 253
	{{/isSingleListener}}
	{{#isMultiListener}}
254
	{{classCName}}Cbs *cbs = {{cPrefix}}_get_current_callbacks({{firstParam}});
255
	{{/isMultiListener}}
256 257 258 259
	jobject jlistener = (jobject) {{cPrefix}}_cbs_get_user_data(cbs);

	if (jlistener == NULL) {
		ms_warning("{{name}}() notification without listener");
260
		return{{returnIfFail}};
261 262 263 264 265 266 267
	}

	jclass jlistenerClass = (jclass) env->GetObjectClass(jlistener);
	jmethodID jcallback = env->GetMethodID(jlistenerClass, "{{jname}}", "{{jparams}}");
	env->DeleteLocalRef(jlistenerClass);

	{{#jobjects}}
268
	jobject j_{{objectName}} = get{{className}}(env, (Linphone{{className}} *){{objectName}});
269 270
	{{/jobjects}}
	{{#jenums}}
271
	LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
272 273 274 275 276 277
	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}}
	
278 279 280 281 282 283 284 285 286 287
	{{#hasReturn}}{{jniUpcallType}} jni_upcall_result = {{/hasReturn}}env->{{jniUpcallMethod}}(jlistener, jcallback, {{params_impl}});
	{{#hasReturn}}
	{{#isJniUpcallObject}}
	{{return}} c_upcall_result = NULL;
	if (jni_upcall_result) c_upcall_result = ({{return}})GetObjectNativePtr(env, jni_upcall_result);
	{{/isJniUpcallObject}}
	{{#isJniUpcallBasicType}}
	{{return}} c_upcall_result = ({{return}}) jni_upcall_result;
	{{/isJniUpcallBasicType}}
	{{/hasReturn}}
288 289 290 291 292 293 294 295 296 297 298 299 300

	{{#jobjects}}
	if (j_{{objectName}}) {
		env->DeleteLocalRef(j_{{objectName}});
	}
	{{/jobjects}}
	{{#jstrings}}
	if (j_{{stringName}}) {
		env->DeleteLocalRef(j_{{stringName}});
	}
	{{/jstrings}}

	handle_possible_java_exception(env, jlistener);
301
	{{#hasReturn}}
302
	return c_upcall_result;
303
	{{/hasReturn}}
304 305
}

306 307 308
{{/callbacks}}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

309 310 311 312
#ifdef __cplusplus
extern "C" {
#endif

313 314
{{#interfaces}}
{{#isSingleListener}}
315
void {{jniPackage}}{{className}}Impl_setListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) {
316 317
{{/isSingleListener}}
{{#isMultiListener}}
318
void {{jniPackage}}{{className}}Impl_addListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) {
319 320 321 322
{{/isMultiListener}}
	{{classCName}} *cptr = ({{classCName}}*)ptr;
	jobject listener = env->NewGlobalRef(jlistener);
	{{#isSingleListener}}
323
	{{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks(cptr);
324 325 326 327 328 329 330 331
	{{/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}}
332 333 334
	{{#isMultiListener}}
	{{cPrefix}}_add_callbacks(cptr, cbs);
	{{/isMultiListener}}
335 336 337
}
{{#isMultiListener}}

338
void {{jniPackage}}{{className}}Impl_removeListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) {
339
	{{classCName}} *cptr = ({{classCName}}*)ptr;
Sylvain Berfini's avatar
Sylvain Berfini committed
340
	const bctbx_list_t *cbs_list = {{cPrefix}}_get_callbacks_list(cptr);
341
	bctbx_list_t *it;
342
	for (it = (bctbx_list_t *)cbs_list; it != NULL; it = it->next) {
343 344 345 346 347 348
		{{classCName}}Cbs *cbs = ({{classCName}}Cbs *)it->data;
		if ({{cPrefix}}_cbs_get_user_data(cbs) == jlistener) {
			{{cPrefix}}_remove_callbacks(cptr, cbs);
			break;
		}
	}	
349 350 351
}
{{/isMultiListener}}

352 353
{{/interfaces}}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
354 355 356 357 358 359 360 361

jobject {{jni_package}}CoreImpl_getMediastreamerFactory(JNIEnv *env, jobject thiz, jlong ptr) {
	LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
	MSFactory *factory = linphone_core_get_ms_factory((LinphoneCore*)ptr);
	return env->NewObject(ljb->ms_factory_class, ljb->ms_factory_class_constructor, (jlong)factory);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Sylvain Berfini's avatar
Sylvain Berfini committed
362 363 364

{{#methods}}
{{return}} {{name}}({{params}}) {
365
	{{#notStatic}}{{classCName}} *cptr = ({{classCName}}*)ptr;{{/notStatic}}{{#strings}}
Sylvain Berfini's avatar
Sylvain Berfini committed
366
	const char* c_{{string}} = GetStringUTFChars(env, {{string}});
367 368 369
	{{/strings}}{{#bytes}}
	{{bytesargtype}} c_{{bytesargname}} = ({{bytesargtype}})env->GetByteArrayElements({{bytesargname}}, NULL);
	{{/bytes}}{{#objects}}
Sylvain Berfini's avatar
Sylvain Berfini committed
370 371
	{{objectClassCName}}* c_{{object}} = NULL;
	if ({{object}}) c_{{object}} = ({{objectClassCName}}*)GetObjectNativePtr(env, {{object}});
372 373
	{{/objects}}{{#lists}}
	bctbx_list_t *bctbx_list_{{list}} = NULL;
374 375
	int {{list}}_count = env->GetArrayLength({{list}});
	for (int i=0; i < {{list}}_count; i++) {
376 377 378 379 380 381 382 383 384 385
		{{#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);
386
		bctbx_list_{{list}} = bctbx_list_append(bctbx_list_{{list}}, ({{objectClassCName}} *)GetObjectNativePtr(env, obj));
387 388 389
		{{/isObjList}}
	}
	{{/lists}}{{#hasListReturn}}
390
	const bctbx_list_t *list = {{c_name}}({{#notStatic}}cptr{{/notStatic}}{{params_impl}});
391
	size_t count = bctbx_list_size(list);
392
	{{#isRealObjectArray}}
393
	LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
394 395
	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}}
396 397 398
	for (size_t i = 0; i < count; i++) {
		{{#isRealObjectArray}}
		{{objectClassCName}}* c_object = ({{objectClassCName}}*)list->data;
399
		jobject object = get{{objectClassName}}(env, c_object);
400
		{{/isRealObjectArray}}
401 402
		{{#isStringObjectArray}}const char *cstring = (const char *)list->data;
		jstring object = cstring ? env->NewStringUTF(cstring) : 0;{{/isStringObjectArray}}
403
		if (object != 0) {
404
			env->SetObjectArrayElement(jni_list_result, (int)i, object);
405 406 407 408
			{{#isRealObjectArray}}env->DeleteLocalRef(object);{{/isRealObjectArray}}
		}
		list = bctbx_list_next(list);
	}
409 410 411 412 413 414 415 416
	{{/hasListReturn}}{{#hasByteArrayReturn}}
	{{c_type_return}} jni_result = {{c_name}}({{#notStatic}}cptr{{/notStatic}}{{params_impl}});
	if (!jni_result) return NULL;
	size_t jni_result_length = strlen((const char *)jni_result);
	jbyteArray array = env->NewByteArray((int)jni_result_length);
	env->SetByteArrayRegion(array, 0, (int)jni_result_length, (const jbyte*)jni_result);
	return array;
	{{/hasByteArrayReturn}}{{#hasStringReturn}}
417 418 419 420
	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}};
421
	{{/hasNormalReturn}}{{#strings}}
Sylvain Berfini's avatar
Sylvain Berfini committed
422
	ReleaseStringUTFChars(env, {{string}}, c_{{string}});
423 424 425
	{{/strings}}{{#bytes}}
	env->ReleaseByteArrayElements({{bytesargname}}, (jbyte*)c_{{bytesargname}}, JNI_ABORT);
	{{/bytes}}{{#hasReturn}}return jni_result;{{/hasReturn}}{{#hasListReturn}}return jni_list_result;{{/hasListReturn}}
Sylvain Berfini's avatar
Sylvain Berfini committed
426 427
}

428 429 430 431 432
{{/methods}}

#ifdef __cplusplus
}
#endif