修改linphone-sdk-android-上篇
前言
记录修改linphone-sdk-android过程,计划分为上、中、下三篇
本文是上篇,本篇仅记录下书问题2的初步排查过程,尽量描述排查问题过程中的思路与方向
余下两篇记录问题1、2的修改过程
背景
接上文编译linphone-sdk-android
项目中使用的linphone-sdk-android版本为4.5.x,使用过程中发现以下两个问题:
- 打开音频编解码G722、G729时,发起呼叫的INVITE SDP中,没有G722、G729的RTP MAP,当时以为是linphone的bug,后面看源码及查资料发现可能不是bug,这里先按下不表
- 使用sdk提供的JavaLogger输出日志时,伪代码:
mFactory.getLoggingService().addListener(mAndroidLoggingService);
,偶现JNI崩溃问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| --------- beginning of crash 2022-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 00000008 2022-04-11 14:16:22.457 3756-3756/? A/DEBUG: r4 89aff978 r5 00000006 r6 89aff920 r7 0000010c 2022-04-11 14:16:22.459 3756-3756/? A/DEBUG: r8 00000043 r9 aaa7eef0 sl 0000000a fp 89d04400 2022-04-11 14:16:22.461 3756-3756/? A/DEBUG: ip 0000000b sp 89afef88 lr ab175857 pc ab1780c0 cpsr 600b0010 2022-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.so 2022-04-11 14:16:22.533 3756-3756/? A/DEBUG: #14 pc 005ef911 /data/app/com.guodong.android.linphone-1/lib/arm/liblinphone.so 2022-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.so 2022-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.so 2022-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.so 2022-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
文本的文件
1 2 3 4 5
| $ 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.cc
,getLoggingService()
方法体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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) { env->DeleteWeakGlobalRef((jobject)up); 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
的文件
1 2 3 4 5 6 7
| $ 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
中的实现对比,锁定了以下代码即为自动生成的模板代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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 ref 316 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 pystache
,pystache
是mustache
的Python实现,mustache
的语法比较简单,在github
上看一眼文档,再跟着源码比着葫芦画瓢,应该就差不多了
好的,学习完mustache
的语法,了解到高级用法可以传一个配置文件进行渲染,正好与jni.mustache
同级目录中有个genwrapper.py
文件,打开此文件,里面定义了很多类,其中有个Jni
的类,里面有些字段与jni.mustache
中的标签正好对应,就是它了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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.py
的Jni
类中新增一个isLoggingService
字段表示是否是getLoggingService()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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
后与NULL
、nullptr
比较的结果,判断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
是否为空,为空则删除全局弱引用,再创建保存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| {{#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; }
|
好的,修改完源码保存后,就可以编译了
1 2
| $ cd linphone-sdk/build/ $ cmake --build . --parallel 8
|
等待编译完成,拷贝出来编译好的aar
,放到Android Studio中运行,查看Logcat输出