Commits (8)
......@@ -25,6 +25,7 @@ if (ENABLE_DOC)
set(doc_output_dir ${CMAKE_CURRENT_BINARY_DIR}/build)
set(reference_doc_source_dir ${doc_source_dir}/reference)
set(static_documentation_files
guides/android_portability.rst
guides/authentication.rst
guides/buddy_list.rst
guides/call_control.rst
......
Android portability
===============
Video
-----
To be able to display remote and locally captured video streams on Android, you have to give the Core 2 TextureViews.
These objects must be given using :java:meth:`setNativeVideoWindowId` and :java:meth:`setNativePreviewWindowId`.
Here's the complete code:
.. code-block:: java
Core mCore; // Get it the way you prefer
TextureView mVideoView = view.findViewById(R.id.videoSurface);
TextureView mCaptureView = view.findViewById(R.id.videoCaptureSurface);
mCore.setNativeVideoWindowId(mVideoView);
mCore.setNativePreviewWindowId(mCaptureView);
\ No newline at end of file
......@@ -47,6 +47,7 @@ Beginners' guides
guides/event_api
guides/misc
guides/ios_portability
guides/android_portability
Code samples
......
......@@ -2004,12 +2004,12 @@ static void deal_with_jwe_auth_module(const char *jwe, bool_t invalid_jwe, bool_
}
void use_jwe_auth_module(void) {
static const char jwe[] = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2Iiwia2lkIjoiOFp0TTNUQjJYN2YxRTBoSDk3dzlCelNvai1fck1qXzVpQnlPVG9ZcWltOCJ9.YIkpmyFKUJaSIBXh8ABjAeymbMN21VAGr7qXFmek0l8Oh3bEGBuf5YggQWP1G55V00WI8KESxm3LJnqzf-L3FDm3S8D-dLeR7GiJgJtJqED6KKf9U86aLo6UZPmh8xIdfVqLFDUeDQQwy3zwZw-MwY_xtDn_RR2u_W5bmWL-t1-A-xTIw6TEwdjqe8X_D0CuhcPx-virV3RBUHwjSO43vsdHMqLExDXIk95CuQOcUJufZJMu0q5KmpuvDSVesf6ZcmKBEVnkIlSbgAl_Hsv51RXPUT3rFsNy0LSEIByyF-zO6u_L6jpqlt8DxKc6aefa9-4KvyaxU1K7AApYZKh2TQ.fjXkk_TeGhfakvKQ.GP8RivXqe5g4OQhvzlvJ-l2jxuRziLWFNohmFIkZoXwL2mvnEqq71GWYr6_X0V7Z3I7nkMKDwhOfBpKbjnDKK-x1BEUmOmTwaJqeX_lJlZeZkXl597jtBXN3fH5vdccRvoxVFHT0DfZcEA.Km01D704Zl-J28hQJ05dEA";
static const char jwe[] = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2Iiwia2lkIjoiOFp0TTNUQjJYN2YxRTBoSDk3dzlCelNvai1fck1qXzVpQnlPVG9ZcWltOCJ9.d1iXAnX4_mZ_A4Iptxbl4uka6JDa5QbWS5rk8ETap6ytTx_Fse2R_piGgePv_1TsG36SkPGbUANlKmgYr5mNkvGvjPWKNizQF_GrT5-nuQxIEq9ciYK-ELg9vahgYcdsiGu-3xn8tA2Z0s2WjVy3iY_qy6Rsab-i5nsiyudCBUQ0BLNfW4_IFP52EfdaxtspEnF7iW3fIV8VFoMfyUE2J1gw6hAf9XPkgh-HRb_umJorT9iPU5zZU7ZpfGwEAt5G-wFrVBMmdMxx1VYVKAWInSVq9W-yDqruAWLKKAlYZYQZIcU6w30x4B8KWdNEhpV-z-3GCrFRO96BcpAhEYCnJA.jObnmdLM4EVS-odJ.jV4YRAi8fA4h4e_XBFv14wpST5-Phw5fikzQZWr-G1I5THSDlun2ef42s3ZlC_kUMgTsg1NSm-CZ9XOLtADK7Krl2hKe3Ax4P6gNBGCGgP_O50pWmkLYngz6PxHQk37nk_qw6tnpCdU-rg.RKjeuRcjSvcRuUtn3dPnbg";
deal_with_jwe_auth_module(jwe, FALSE, FALSE);
}
void use_jwe_auth_module_with_invalid_oid(void) {
static const char jwe[] = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2Iiwia2lkIjoiOFp0TTNUQjJYN2YxRTBoSDk3dzlCelNvai1fck1qXzVpQnlPVG9ZcWltOCJ9.YIkpmyFKUJaSIBXh8ABjAeymbMN21VAGr7qXFmek0l8Oh3bEGBuf5YggQWP1G55V00WI8KESxm3LJnqzf-L3FDm3S8D-dLeR7GiJgJtJqED6KKf9U86aLo6UZPmh8xIdfVqLFDUeDQQwy3zwZw-MwY_xtDn_RR2u_W5bmWL-t1-A-xTIw6TEwdjqe8X_D0CuhcPx-virV3RBUHwjSO43vsdHMqLExDXIk95CuQOcUJufZJMu0q5KmpuvDSVesf6ZcmKBEVnkIlSbgAl_Hsv51RXPUT3rFsNy0LSEIByyF-zO6u_L6jpqlt8DxKc6aefa9-4KvyaxU1K7AApYZKh2TQ.fjXkk_TeGhfakvKQ.GP8RivXqe5g4OQhvzlvJ-l2jxuRziLWFNohmFIkZoXwL2mvnEqq71GWYr6_X0V7Z3I7nkMKDwhOfBpKbjnDKK-x1BEUmOmTwaJqeX_lJlZeZkXl597jtBXN3fH5vdccRvoxVFHT0DfZcEA.Km01D704Zl-J28hQJ05dEA";
static const char jwe[] = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2Iiwia2lkIjoiOFp0TTNUQjJYN2YxRTBoSDk3dzlCelNvai1fck1qXzVpQnlPVG9ZcWltOCJ9.W1csJX4veF4SgMAUpBL5QLOibXfH6z6AEuebB9FpfzcANAtliYIggo1mbKzZVECZj4O5M158SIU4O4A5Jk9rAeriKGKUjgDpgBJlV7aKhBRvp-fGVF52BynQ7UFDZYZoPLQh17_PSNI4hzDTigVAI0OYRGQLsSoHCNaP72Zf5hNi7iJBTqL8cmViQqGTOczFxF9vacIDAcXkPhiCSy-3Q4Jsl5JJnESUc_DHrA2b2RVde_TI0koweUPs9SrS63w4XkLvOv_MVEeIZPksZv3hNPnvaut7GrytHWbz2LcYP4Gs8MQbAxIZ-rtl0x0af2KLOfI1xOEvMYIyrg0Vu4D4EQ.7--QW4e7N43miyFp.Fuu6sFIL6tCwdLiK6m-rKF6P_kp0qlPt_gZyPHBK62_v2aQoCU9Sf3RfCOePSzE0Y9fbMt2_4eTcdleJeaOzMTcZqKdNEZKpfdaDe77tobMNx6dzvQSW-Pk6FxwaBSr7fA4uFIzThkZ2cP8.dhd48xH9hZ5OQo6SNHwCmg";
deal_with_jwe_auth_module(jwe, FALSE, TRUE);
}
......@@ -2024,7 +2024,7 @@ void use_jwe_auth_module_with_no_exp(void) {
}
void use_jwe_auth_module_with_invalid_attr(void) {
static const char jwe[] = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2Iiwia2lkIjoiOFp0TTNUQjJYN2YxRTBoSDk3dzlCelNvai1fck1qXzVpQnlPVG9ZcWltOCJ9.Lj-1x8-c1BJWSvwYQtMWDV2fhGRNZ0B6auR1QgVRw2e4kxEToSSjZJAXKudAvNII0FUtPU6hf0uvuDuc9VQHRDCV3DSg0iLxVfcLJcnC0hygHnjBuVJiglqfKVfXnaLwadkRVS7Q3A52sC_YZnk0aTZKpefu1Zc_kr_llKH_pIEZDd2FL1M3XZXNiiemM1Lwq5sbRPYqxOHhVySOIUcAzUkPJpzgaIMGVwn4cwvDL58inp9tyUDak6BV6sFoSovpJ4w4HDIRLUj8BXcBZiyXlzj06YUiZVkO2JPRZR_KBPdZtKAE5KoWGe074e5q-L_f6_i3Y0psN9hI8FZfzy0nvw.FAtaQpIlLCx19mPq.TJS8q2aCeLT-oLSy24eRa2FN07AIjlymwruBvYROFeU-_UgomwIcPQhVvfE0h_QyJNVcY1iwBnCBe8KDbVHUrfHhQcCHJmOHXZ7ouzINBPk0qqr-ubCroHuV3J-prIa10qwvEzjmFBAPEw4.6cOmc0txLOCpIXa3WOkRSA";
static const char jwe[] = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2Iiwia2lkIjoiOFp0TTNUQjJYN2YxRTBoSDk3dzlCelNvai1fck1qXzVpQnlPVG9ZcWltOCJ9.Db81FlIVyW2N2DViRoA1pUKtT9M-0uZEHa8duBt1kRnN8gw1BpFfKvDb7j8DJeTlfIuYMhmzBH5Qr6olWWqVX9dhC2-xopJ10Zpk0o48XmCviimhrl_sSM-WfthP7FTgxXBP-3PxEIE6wblnjhesSM3Q9nImyQ4xa_5ObYLo1JP35d2iV8irnQba6YxvZqQCeoxiW7YLzGPWtWF9YqXOOALgpwyCLt4JeTrP-m6RggdwfQQqdXbksF6nvg1vK54-osBGE3jwvv6dQwFLwe-TBYug1w7H7IC-dXIPon5OESx_81aW2u-j8S5LSyOjZOLTQj_WMS1MX5CEFtYxpgMlIQ.3WWvsaJzQdqVMOXJ.xmwOL6voeKXS6lLaC8EMQGfaacs1sN9Vd33wcIRfeKFfn0nk1lCtb4IsxSFEaMvaM5rjCN-K2kHTFPNitHAXrex5zhUf9Hq_gMMx9vago63DL674SDQ_dDJiYGBqs8SKxg.zm80aJUoeiJ5MiNMq8MSUQ";
deal_with_jwe_auth_module(jwe, TRUE, FALSE);
}
......
......@@ -645,7 +645,8 @@ class Jni(object):
'className': javaClass.className,
'classCName': javaClass.cName,
'classImplName': javaClass.classImplName,
'refCountable': javaClass.refCountable
'refCountable': javaClass.refCountable,
'notRefCountable': not javaClass.refCountable
}
self.objects.append(obj)
......
......@@ -209,11 +209,36 @@ class {{classImplName}} {{#isLinphoneFactory}}extends{{/isLinphoneFactory}}{{#is
{{/isLinphoneCore}}
{{#isNotLinphoneFactory}}
private native void unref(long ptr);
private native boolean unref(long ptr);
protected void finalize() throws Throwable {
/* Considering the following scenario:
User scrolls fast in a chat message list that contains a lot of images,
so the VM will garbage collect often due to the images taking much space.
The view itself only has a reference on the EventLogs, not the ChatMessages directly,
so each call of onBindView will call eventLog.getChatMessage() and the reference will only be temporary.
It has been observed a race condition can occurs in which the JNI level can obtain a LocalRef from a WeakGlobalRef
of an object that has been scheduled for destruction and for which the finalize() method will be called shortly after.
The result is a Java object that has been finalized can have it's methods be called after the finalize() has terminated.*/
/*The workaround we made here is to keep the nativePtr value even after the unref() only if the object is still reffed by the SDK,
so the few methods calls following the finalize() will still work because we know the native pointer still exists.*/
/*This solution will work as long as the objects still have a ref at C level, which is the case with ChatMessages because they are hold by the Events.
Should we have this problem again in a situation where the C no longer holds a reference (resulting in the C object being destroyed by finalize()),
another solution is possible (but more complex to implement).
This solution would consist in implementing at C level, a queue of belle_sip_object_t that are loosing their last ref.
Instead of being deleted immediately by belle_sip_object_unref() calling belle_sip_object_delete(),
unref() would place the object onto this "C finalizer queue", the delete() call would then be performed later,
by a hook added into the main android looper.
This solution should work because the actual destruction will be performed from the looper context,
so in a place where the local java object causing our troubles no longer exists for sure.
This solution has however the inconvenience that it can work within an application that uses a single thread to call liblinphone API.*/
if (nativePtr != 0) {
unref(nativePtr);
nativePtr = 0;
boolean destroyed = unref(nativePtr);
if (destroyed) {
nativePtr = 0;
}
}
super.finalize();
}
......
......@@ -69,6 +69,15 @@ static jlong GetObjectNativePtr(JNIEnv *env, jobject object) {
return nativePtr;
}
static void LogJavaObjectForNativePtr(JNIEnv *env, jobject obj, jlong cptr) {
jclass javaObject = env->FindClass("java/lang/Object");
jmethodID javaObjectToString = env->GetMethodID(javaObject, "toString", "()Ljava/lang/String;");
jstring javaObjectName = (jstring)env->CallObjectMethod(obj, javaObjectToString);
const char *name = GetStringUTFChars(env, javaObjectName);
bctbx_debug("Java object name is %s for native pointer %p", name, (void *)cptr);
ReleaseStringUTFChars(env, javaObjectName, name);
}
extern "C" {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
......@@ -275,18 +284,19 @@ JNIEXPORT jobject JNICALL get{{className}}(JNIEnv *env, {{classCName}} *cptr, bo
return jobj;
}
JNIEXPORT void JNICALL Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong ptr) {
JNIEXPORT jboolean JNICALL Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong ptr) {
{{classCName}} *cptr = ({{classCName}}*)ptr;
if (cptr == 0) {
bctbx_error("{{classCName}} C ptr is null!");
return;
bctbx_error("Java_{{jniPrefix}}{{classImplName}}_unref's {{classCName}} C ptr is null!");
return TRUE;
}
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, nullptr, nullptr);
if (wref) {
env->DeleteWeakGlobalRef(wref);
}
{{#refCountable}}{{cPrefix}}_unref(cptr);{{/refCountable}}
{{#refCountable}}return belle_sip_object_unref_2(cptr) == 1;{{/refCountable}}
{{#notRefCountable}}return FALSE;{{/notRefCountable}}
}
{{/objects}}
......@@ -401,7 +411,7 @@ static {{return}} {{callbackName}}({{params}}) {
JNIEXPORT void JNICALL Java_{{jniPackage}}{{className}}Impl_setListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) {
{{classCName}} *cptr = ({{classCName}}*)ptr;
if (cptr == 0) {
bctbx_error("{{classCName}} C ptr is null!");
bctbx_error("Java_{{jniPackage}}{{className}}Impl_setListener's {{classCName}} C ptr is null!");
return;
}
{{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks(cptr);
......@@ -436,7 +446,7 @@ JNIEXPORT void JNICALL Java_{{jniPackage}}{{className}}Impl_addListener(JNIEnv*
if (jlistener == nullptr) return;
{{classCName}} *cptr = ({{classCName}}*)ptr;
if (cptr == 0) {
bctbx_error("{{classCName}} C ptr is null!");
bctbx_error("Java_{{jniPackage}}{{className}}Impl_addListener's {{classCName}} C ptr is null!");
return;
}
jobject listener = env->NewGlobalRef(jlistener);
......@@ -452,7 +462,7 @@ JNIEXPORT void JNICALL Java_{{jniPackage}}{{className}}Impl_addListener(JNIEnv*
JNIEXPORT void JNICALL Java_{{jniPackage}}{{className}}Impl_removeListener(JNIEnv* env, jobject thiz, jlong ptr, jobject jlistener) {
{{classCName}} *cptr = ({{classCName}}*)ptr;
if (cptr == 0) {
bctbx_error("{{classCName}} C ptr is null!");
bctbx_error("Java_{{jniPackage}}{{className}}Impl_removeListener's {{classCName}} C ptr is null!");
return;
}
const bctbx_list_t *cbs_list = {{cPrefix}}_get_callbacks_list(cptr);
......@@ -486,7 +496,7 @@ JNIEXPORT {{return}} JNICALL {{name}}({{params}}) {
{{#notStatic}}{{classCName}} *cptr = ({{classCName}}*)ptr;
{{#isLinphoneFactory}}cptr = linphone_factory_get();{{/isLinphoneFactory}}
if (cptr == nullptr) {
bctbx_error("{{classCName}} C ptr is null!");
bctbx_error("{{name}}'s {{classCName}} C ptr is null!");
{{#hasListReturn}}
{{#isRealObjectArray}}
LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
......@@ -571,7 +581,7 @@ JNIEXPORT {{return}} JNICALL {{name}}({{params}}) {
JNIEXPORT void JNICALL Java_org_linphone_core_CallImpl_setNativeVideoWindowId(JNIEnv *env, jobject thiz, jlong ptr, jobject id) {
LinphoneCall *cptr = (LinphoneCall*)ptr;
if (cptr == 0) {
bctbx_error("LinphoneCall C ptr is null!");
bctbx_error("Java_org_linphone_core_CallImpl_setNativeVideoWindowId's LinphoneCall C ptr is null!");
return;
}
LinphoneCore *lc = linphone_call_get_core(cptr);
......@@ -581,7 +591,7 @@ JNIEXPORT void JNICALL Java_org_linphone_core_CallImpl_setNativeVideoWindowId(JN
JNIEXPORT void JNICALL Java_org_linphone_core_CoreImpl_setNativePreviewWindowId(JNIEnv *env, jobject thiz, jlong ptr, jobject id) {
LinphoneCore *cptr = (LinphoneCore*)ptr;
if (cptr == 0) {
bctbx_error("LinphoneCore C ptr is null!");
bctbx_error("Java_org_linphone_core_CoreImpl_setNativePreviewWindowId's LinphoneCore C ptr is null!");
return;
}
linphone_core_set_native_preview_window_id(cptr, (void *)id);
......@@ -590,7 +600,7 @@ JNIEXPORT void JNICALL Java_org_linphone_core_CoreImpl_setNativePreviewWindowId(
JNIEXPORT void JNICALL Java_org_linphone_core_CoreImpl_setNativeVideoWindowId(JNIEnv *env, jobject thiz, jlong ptr, jobject id) {
LinphoneCore *cptr = (LinphoneCore*)ptr;
if (cptr == 0) {
bctbx_error("LinphoneCore C ptr is null!");
bctbx_error("Java_org_linphone_core_CoreImpl_setNativeVideoWindowId's LinphoneCore C ptr is null!");
return;
}
linphone_core_set_native_video_window_id(cptr, (void *)id);
......