前言

记录批改linphone-sdk-android过程,打算分为上、中、下三篇

本文是上篇,本篇仅记录下书问题2的初步排查过程,尽量形容排查问题过程中的思路与方向

余下两篇记录问题1、2的批改过程

背景

接上文编译linphone-sdk-android

我的项目中应用的linphone-sdk-android版本为4.5.x,应用过程中发现以下两个问题:

  1. 关上音频编解码G722、G729时,发动呼叫的INVITE SDP中,没有G722、G729的RTP MAP,过后认为是linphone的bug,前面看源码及查资料发现可能不是bug,这里先按下不表
  2. 应用sdk提供的JavaLogger输入日志时,伪代码:mFactory.getLoggingService().addListener(mAndroidLoggingService);,偶现JNI解体问题
    --------- beginning of crash2022-04-11 14:16:22.350 1142-1430/? A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 1430 (RealLinphonePro)2022-04-11 14:16:22.441 3756-3756/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***2022-04-11 14:16:22.442 3756-3756/? A/DEBUG: Build fingerprint: 'Android/rk3288/rk3288:7.1.2/NHG47K/builde03162201:userdebug/test-keys'2022-04-11 14:16:22.444 3756-3756/? A/DEBUG: Revision: '0'2022-04-11 14:16:22.446 3756-3756/? A/DEBUG: ABI: 'arm'2022-04-11 14:16:22.447 3756-3756/? A/DEBUG: pid: 1142, tid: 1430, name: RealLinphonePro  >>> com.guodong.android.linphone <<<2022-04-11 14:16:22.448 3756-3756/? A/DEBUG: signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------2022-04-11 14:16:22.454 3756-3756/? A/DEBUG: Abort message: 'art/runtime/indirect_reference_table.cc:80] JNI ERROR (app bug): accessed deleted WeakGlobal 0x21ab'2022-04-11 14:16:22.456 3756-3756/? A/DEBUG:     r0 00000000  r1 00000596  r2 00000006  r3 000000082022-04-11 14:16:22.457 3756-3756/? A/DEBUG:     r4 89aff978  r5 00000006  r6 89aff920  r7 0000010c2022-04-11 14:16:22.459 3756-3756/? A/DEBUG:     r8 00000043  r9 aaa7eef0  sl 0000000a  fp 89d044002022-04-11 14:16:22.461 3756-3756/? A/DEBUG:     ip 0000000b  sp 89afef88  lr ab175857  pc ab1780c0  cpsr 600b00102022-04-11 14:16:22.509 3756-3756/? A/DEBUG: backtrace:2022-04-11 14:16:22.511 3756-3756/? A/DEBUG:     #00 pc 0004a0c0  /system/lib/libc.so (tgkill+12)2022-04-11 14:16:22.512 3756-3756/? A/DEBUG:     #01 pc 00047853  /system/lib/libc.so (pthread_kill+34)2022-04-11 14:16:22.514 3756-3756/? A/DEBUG:     #02 pc 0001d8b5  /system/lib/libc.so (raise+10)2022-04-11 14:16:22.515 3756-3756/? A/DEBUG:     #03 pc 00019401  /system/lib/libc.so (__libc_android_abort+34)2022-04-11 14:16:22.517 3756-3756/? A/DEBUG:     #04 pc 00017048  /system/lib/libc.so (abort+4)2022-04-11 14:16:22.518 3756-3756/? A/DEBUG:     #05 pc 0031d8cd  /system/lib/libart.so (_ZN3art7Runtime5AbortEPKc+328)2022-04-11 14:16:22.520 3756-3756/? A/DEBUG:     #06 pc 000b5503  /system/lib/libart.so (_ZN3art10LogMessageD2Ev+1134)2022-04-11 14:16:22.521 3756-3756/? A/DEBUG:     #07 pc 001bd0ff  /system/lib/libart.so (_ZN3art22IndirectReferenceTable17AbortIfNoCheckJNIERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE+134)2022-04-11 14:16:22.523 3756-3756/? A/DEBUG:     #08 pc 0023ecaf  /system/lib/libart.so (_ZNK3art22IndirectReferenceTable10GetCheckedEPv+250)2022-04-11 14:16:22.525 3756-3756/? A/DEBUG:     #09 pc 0023c05b  /system/lib/libart.so (_ZN3art9JavaVMExt16DecodeWeakGlobalEPNS_6ThreadEPv+30)2022-04-11 14:16:22.526 3756-3756/? A/DEBUG:     #10 pc 00337679  /system/lib/libart.so (_ZNK3art6Thread13DecodeJObjectEP8_jobject+164)2022-04-11 14:16:22.528 3756-3756/? A/DEBUG:     #11 pc 00265843  /system/lib/libart.so (_ZN3art3JNI11NewLocalRefEP7_JNIEnvP8_jobject+406)2022-04-11 14:16:22.530 3756-3756/? A/DEBUG:     #12 pc 0060eff9  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (getLoggingService+88)2022-04-11 14:16:22.531 3756-3756/? A/DEBUG:     #13 pc 0061a977  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so2022-04-11 14:16:22.533 3756-3756/? A/DEBUG:     #14 pc 005ef911  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so2022-04-11 14:16:22.535 3756-3756/? A/DEBUG:     #15 pc 00025437  /data/app/com.guodong.android.linphone-1/lib/arm/libbctoolbox.so (bctbx_logv+182)2022-04-11 14:16:22.536 3756-3756/? A/DEBUG:     #16 pc 006f5061  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so2022-04-11 14:16:22.538 3756-3756/? A/DEBUG:     #17 pc 006f51d9  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (wake_lock_acquire+152)2022-04-11 14:16:22.539 3756-3756/? A/DEBUG:     #18 pc 006601c5  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so2022-04-11 14:16:22.541 3756-3756/? A/DEBUG:     #19 pc 00660b27  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_client_transaction_init+138)2022-04-11 14:16:22.542 3756-3756/? A/DEBUG:     #20 pc 006fb1d3  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_nict_new+30)2022-04-11 14:16:22.544 3756-3756/? A/DEBUG:     #21 pc 0065c3a7  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_provider_create_client_transaction+46)2022-04-11 14:16:22.546 3756-3756/? A/DEBUG:     #22 pc 0065dec3  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so2022-04-11 14:16:22.547 3756-3756/? A/DEBUG:     #23 pc 0065daf7  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (belle_sip_refresher_refresh+28)2022-04-11 14:16:22.549 3756-3756/? A/DEBUG:     #24 pc 005fac23  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (linphone_proxy_config_refresh_register+34)2022-04-11 14:16:22.550 3756-3756/? A/DEBUG:     #25 pc 005e9fc5  /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (linphone_core_refresh_registers+40)2022-04-11 14:16:22.552 3756-3756/? A/DEBUG:     #26 pc 019fc3bf  /data/app/com.guodong.android.linphone-1/oat/arm/base.odex (offset 0x18de000)2022-04-11 14:16:23.096 227-255/? I/AudioFlinger: BUFFER TIMEOUT: remove(4099) from active list on thread 0xabd03e00

剖析

问题2

通过解体日志第27行2022-04-11 14:16:22.530 3756-3756/? A/DEBUG: #12 pc 0060eff9 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so (getLoggingService+88)能够判断解体是在getLoggingService()这个办法里

如此须要先找到getLoggingService()办法的源码,先在Source Insight中搜寻getLoggingService(),搜寻一圈发现没有,猜测此办法应该是编译后主动生成的

在Ubuntu编译环境里找找,通过find命令查找蕴含getLoggingService文本的文件

$ cd linphone-sdk/build/$ find . -type f | xargs grep "getLoggingService"./WORK/android-armv7/Build/linphone/wrappers/java/src/linphone_jni.cc:JNIEXPORT jobject JNICALL getLoggingService(JNIEnv *env, LinphoneLoggingService *cptr, bool_t takeref) {./WORK/android-armv7/Build/linphone/wrappers/java/src/linphone_jni.cc:    jobject j_logService = getLoggingService(env, (LinphoneLoggingService *)logService, TRUE);./WORK/android-armv7/Build/linphone/wrappers/java/src/linphone_jni.cc:    jobject jni_result = (jobject)getLoggingService(env, (LinphoneLoggingService *)linphone_logging_service_get(), TRUE);

找到了bingo~

关上linphone_jni.ccgetLoggingService()办法体如下:

JNIEXPORT jobject JNICALL getLoggingService(JNIEnv *env, LinphoneLoggingService *cptr, bool_t takeref) {    jobject jobj = nullptr;    if (cptr != nullptr) {        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 linphone_logging_service_class = ljb->linphone_logging_service_class;        jmethodID linphone_logging_service_constructor = ljb->linphone_logging_service_class_constructor;        if (up == nullptr) {            jobj = env->NewObject(linphone_logging_service_class, linphone_logging_service_constructor, (jlong)cptr);            belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);            if (takeref)                linphone_logging_service_ref(cptr);        } else {            jobj = env->NewLocalRef((jobject)up);            if (jobj == nullptr) {                // Delete weak ref ?                env->DeleteWeakGlobalRef((jobject)up);                // takes implicit local ref                jobj = env->NewObject(linphone_logging_service_class, linphone_logging_service_constructor, (jlong)cptr);                belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);                if (takeref)                    linphone_logging_service_ref(cptr);            }        }    }    return jobj;}

嗯,看起来没啥问题,也有判空解决,一时间摸不着脉络,先在getLoggingService()办法中加点日志输入看看吧

后面说linphone_jni.cc是主动生成的,当初须要找到主动生成linphone_jni.cc的代码,依据解体日志能够发现getLoggingService()linphone.so中,而源码中有liblinphone这个目录,先在这个目录下找找吧,通过find命令查找文件名中蕴含jni的文件

$ cd linphone-sdk/liblinphone/$ find . -name *jni*./coreapi/linphonecore_jni.cc./tools/lpc2xml_jni.cc./tools/xml2lpc_jni.cc./tools/my_jni.h./wrappers/java/jni.mustache

查找进去多个文件,既然linphone_jni.cc是主动生成的,所以能够确定.cc.h后缀的几个文件必定不是,排除后只剩下这个jni.mustache文件,关上文件vim ./wrappers/java/jni.mustache正文第一行就是linphone_jni.cc,太棒了,找到主动生成的代码了

通过一些查看,并与linphone_jni.cc中的实现比照,锁定了以下代码即为主动生成的模板代码:

290 {{#objects}}291 JNIEXPORT jobject JNICALL get{{className}}(JNIEnv *env, {{classCName}} *cptr, bool_t takeref) {292         jobject jobj = nullptr;293 294         if (cptr != nullptr) {295                 void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);296                 LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());297                 if (!ljb) {298                         ljb = new LinphoneJavaBindings(env);299                         linphone_factory_set_user_data(linphone_factory_get(), ljb);300                 }301 302                 jclass {{cPrefix}}_class = ljb->{{cPrefix}}_class;303                 jmethodID {{cPrefix}}_constructor = ljb->{{cPrefix}}_class_constructor;304 305                 if (up == nullptr) {306                         jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);307                         belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);308                         if (takeref)309                                 {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}310                 } else {311                         jobj = env->NewLocalRef((jobject)up);312                         if (jobj == nullptr) {313                                 // Delete weak ref ?314                                 env->DeleteWeakGlobalRef((jobject)up);315                                 // takes implicit local ref316                                 jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);317                                 belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);318                                 if (takeref)319                                         {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}320                         }321                 }322         }323         return jobj;324 }

这里linphone-sdk应用了mustache,还记得在上篇编译Linphone-SDK-Android中有装置pip3 install pystachepystachemustache的Python实现,mustache的语法比较简单,在github上看一眼文档,再跟着源码比着葫芦画瓢,应该就差不多了

好的,学习完mustache的语法,理解到高级用法能够传一个配置文件进行渲染,正好与jni.mustache同级目录中有个genwrapper.py文件,关上此文件,外面定义了很多类,其中有个Jni的类,外面有些字段与jni.mustache中的标签正好对应,就是它了

class Jni:    def add_object(self, javaClass):        if javaClass.className == 'Factory':            return        obj = {            'jniPrefix': self.jni_package,            'jniPath': self.jni_path,            'cPrefix': javaClass.cPrefix,            'className': javaClass.className,            'classCName': javaClass.cName,            'classImplName': javaClass.classImplName,            'refCountable': javaClass.refCountable,            'notRefCountable': not javaClass.refCountable,        }        self.objects.append(obj)

happy~万事具备,当初能够批改源码,增加一些日志输入了

因为在jni.mustache中模板办法get{{className}}是能够生成多个以get结尾的办法,当初须要判断{{className}}是不是等于LoggingService,而在mustache中没有发现比拟字符串是否雷同的语法,所以在genwrapper.pyJni类中新增一个isLoggingService字段示意是否是getLoggingService()办法

class Jni:    def add_object(self, javaClass):        if javaClass.className == 'Factory':            return        obj = {            'jniPrefix': self.jni_package,            'jniPath': self.jni_path,            'cPrefix': javaClass.cPrefix,            'className': javaClass.className,            'classCName': javaClass.cName,            'classImplName': javaClass.classImplName,            'refCountable': javaClass.refCountable,            'notRefCountable': not javaClass.refCountable,            'isLoggingService': javaClass.className == 'LoggingService',        }        self.objects.append(obj)

而后批改jni.mustache文件,减少日志,输入up指针指向的地址,up指针转成jobject后与NULLnullptr比拟的后果,判断up是否曾经被回收了

这里说一下代码的大略意思,up指针从belle_sip_object_data_get办法中通过belle_sip_java_user_data_key这个Key获取,能够简略了解为Java中的Map;如果up为空,则调用Java办法创立一个对象,将创立的Java对象通过NewWeakGlobalRef办法转换为全局弱援用,再通过belle_sip_object_data_set办法保存起来;如果up不为空,则强转为jobject,判断jobject是否为空,为空则删除全局弱援用,再创立保留

{{#objects}}JNIEXPORT jobject JNICALL get{{className}}(JNIEnv *env, {{classCName}} *cptr, bool_t takeref) {    jobject jobj = nullptr;    if (cptr != nullptr) {        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;        {{#isLoggingService}}        #ifdef __ANDROID__        __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up = %p", up);                jobject temp_jobj1 = (jobject)up;        jboolean up_available1 = env->IsSameObject(temp_jobj1, NULL);        __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up_available1 = %d", up_available1);                jobject temp_jobj2 = (jobject)up;        jboolean up_available2 = env->IsSameObject(temp_jobj2, nullptr);        __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up_available2 = %d", up_available2);        #endif /* __ANDROID__ */        {{/isLoggingService}}        if (up == nullptr) {            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), nullptr);            if (takeref)                {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}        } else {            jobj = env->NewLocalRef((jobject)up);            if (jobj == nullptr) {                // 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), nullptr);                if (takeref)                    {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}            }        }    }    return jobj;}

好的,批改完源码保留后,就能够编译了

$ cd linphone-sdk/build/$ cmake --build . --parallel 8

期待编译实现,拷贝进去编译好的aar,放到Android Studio中运行,查看Logcat输入