修改linphone-sdk-android-第一篇

前言

記錄修改linphone-sdk-android過程,計劃分為上、中、下三篇

本文是上篇,本篇僅記錄下書問題2的初步排查過程,盡量描述排查問題過程中的思路與方向

余下兩篇記錄問題1、2的修改過程

背景

接上文編譯linphone-sdk-android

項目中使用的linphone-sdk-android版本為4.5.x,使用過程中發(fā)現(xiàn)以下兩個問題:

  1. 打開音頻編解碼G722、G729時,發(fā)起呼叫的INVITE SDP中,沒有G722、G729的RTP MAP,當時以為是linphone的bug,后面看源碼及查資料發(fā)現(xiàn)可能不是bug,這里先按下不表
  2. 使用sdk提供的JavaLogger輸出日志時,偽代碼:mFactory.getLoggingService().addListener(mAndroidLoggingService);,偶現(xiàn)JNI崩潰問題
    --------- 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(),搜索一圈發(fā)現(xiàn)沒有,猜想此方法應該是編譯后自動生成的

在Ubuntu編譯環(huán)境里找找,通過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.cc,getLoggingService()方法體如下:

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是自動生成的,現(xiàn)在需要找到自動生成linphone_jni.cc的代碼,根據(jù)崩潰日志可以發(fā)現(xiàn)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中的實現(xiàn)對比,鎖定了以下代碼即為自動生成的模板代碼:

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 pystachepystachemustache的Python實現(xiàn),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~萬事具備,現(xiàn)在可以修改源碼,添加一些日志輸出了

因為在jni.mustache中模板方法get{{className}}是可以生成多個以get開頭的方法,現(xiàn)在需要判斷{{className}}是不是等于LoggingService,而在mustache中沒有發(fā)現(xiàn)比較字符串是否相同的語法,所以在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指針轉(zhuǎn)成jobject后與NULL、nullptr比較的結(jié)果,判斷up是否已經(jīng)被回收了

這里說一下代碼的大概意思,up指針從belle_sip_object_data_get方法中通過belle_sip_java_user_data_key這個Key獲取,可以簡單理解為Java中的Map;如果up為空,則調(diào)用Java方法創(chuàng)建一個對象,將創(chuàng)建的Java對象通過NewWeakGlobalRef方法轉(zhuǎn)換為全局弱引用,再通過belle_sip_object_data_set方法保存起來;如果up不為空,則強轉(zhuǎn)為jobject,判斷jobject是否為空,為空則刪除全局弱引用,再創(chuàng)建保存

{{#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輸出

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容