linphone-sdk-android网络检测分析
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 8 天,点击查看活动详情
前言
好久没写 linphone-sdk-android
相关的文章了,上一篇文章还是一个月之前,经过上次修改 linphone-sdk-android
后最近没有啥问题发生,本文记录下之前遇到的 linphone
网络问题的坑。
注:笔者的 App 作为 Launcher 启动,目前运行在 Android 7.1.2 API 25 系统上,使用以太网连接。
分析
注:以下源码基于 linphone-sdk-android 4.5.26。
问题描述:在设备重启后,程序偶现发起不了呼叫,提示当前网络不可达,但是当前网络是好的,这就比较奇怪了。
接下来查看设备重启后的日志,发现一些与 linphone 网络相关的:
1 2 3 liblinphone : SIP network reachability state is now DOWN liblinphone : SIP network reachability state is now UP liblinphone : SIP network reachability state is now DOWN
刷新账户注册时也提示网络不可达,日志如下:
1 liblinphone : Refresh register operation not available (network unreachable)
然后执行测试程序调用 Core#isNetworkReachable
方法也返回 false
,发起呼叫时笔者调用了此接口判断网络是否可达,所以出现了呼叫不了,提示当前网络不可达的问题,现在猜测是 linphone
内部维护的网络状态出现了问题,遂去查看下源码中对网络状态的处理。
Core#setNetworkReachable
首先想到 Core#setNetworkReachable
接口,从 setNetworkReachable
下手吧,setNetworkReachable
方法最终会调用 native 的 private native void setNetworkReachable(long var1, boolean var3);
。
在之前的文章中,我们分析了 Core
接口调用的 native 方法一般在 linphone_jni.cc
中实现,我们打开 linphone_jni.cc
,在其中搜索 setNetworkReachable
关键字:
1 2 3 4 5 6 7 8 JNIEXPORT void JNICALL Java_org_linphone_core_CoreImpl_setNetworkReachable (JNIEnv *env, jobject thiz, jlong ptr, jboolean reachable) { LinphoneCore *cptr = (LinphoneCore*)ptr; if (cptr == nullptr ) { bctbx_error ("Java_org_linphone_core_CoreImpl_setNetworkReachable's LinphoneCore C ptr is null!" ); return ; } linphone_core_set_network_reachable (cptr, (bool_t )reachable); }
在 linphone_jni.cc
中调用了 linphone_core_set_network_reachable
,我们跟进去看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void linphone_core_set_network_reachable (LinphoneCore *lc, bool_t is_reachable) { bool_t reachable = is_reachable; lc->sip_network_state.user_state = is_reachable; lc->media_network_state.user_state = is_reachable; if (lc->auto_net_state_mon) reachable = reachable && getPlatformHelpers(lc)->isNetworkReachable(); set_sip_network_reachable(lc, reachable, ms_time(NULL )); set_media_network_reachable(lc, reachable); notify_network_reachable_change(lc); }
在 linphone_core_set_network_reachable
方法中首先是修改了 「SIP 协议层网络状态 - 用户设置的状态」和「媒体传输层网络状态 - 用户设置的状态」,接下来判断是否开启了「网络状态自动检测」配置,若是开启了,则获取自动检测的网络状态并与传入的状态取且值,这里可能会改变传入的值:如果传入 True,而 isNetworkReachable
返回 False,最终 reachable
为 False。
接下来,我们看下 set_sip_network_reachable
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static void set_sip_network_reachable (LinphoneCore* lc,bool_t is_sip_reachable, time_t curtime) { const bctbx_list_t *elem = NULL ; if (lc->sip_network_state.global_state==is_sip_reachable) return ; lc->network_reachable_to_be_notified=TRUE; ms_message("SIP network reachability state is now [%s]" ,is_sip_reachable?"UP" :"DOWN" ); lc->netup_time=curtime; lc->sip_network_state.global_state=is_sip_reachable; }
在 set_sip_network_reachable
方法中,首先判断传入的值是否与已有的值相等,相等则认为没有变化,忽略此次调用,然后打印传入 is_sip_reachable
的值,这行日志对应前面提到的设备重启后与网络相关的日志,最后修改「SIP协议层网络状态 - 全局状态」。
前面提到刷新账户注册时也提示网络不可达,赶巧了,set_sip_network_reachable
方法的下面就是调用刷新账户注册的实现:
1 2 3 4 5 6 7 8 9 10 11 12 void linphone_core_refresh_registers (LinphoneCore* lc) { if (!lc->sip_network_state.global_state) { ms_warning("Refresh register operation not available (network unreachable)" ); return ; } }
在 linphone_core_refresh_registers
方法中,首先就判断了「SIP协议层网络状态 - 全局状态」,如果为 False,则此时网络不可达,输出网络不可达日志。
最后看下 Core#isNetworkReachable
的实现:
1 2 3 4 5 6 bool_t linphone_core_is_network_reachable (LinphoneCore* lc) { return lc->sip_network_state.global_state; }
在 Java 中调用 isNetworkReachable
最终会调用到 linphone_core_is_network_reachable
方法,在 linphone_core_is_network_reachable
方法中,直接返回了「SIP协议层网络状态 - 全局状态」。
在上一节中我们分析是在 set_sip_network_reachable
方法修改的「SIP协议层网络状态 - 全局状态」,除了我们调用 Core#setNetworkReachable
外,应该还有调用方,在 IDE 中分析发现了 linphone_core_set_network_reachable_internal
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void linphone_core_set_network_reachable_internal (LinphoneCore *lc, bool_t is_reachable) { if (lc->auto_net_state_mon) { set_sip_network_reachable(lc, lc->sip_network_state.user_state && is_reachable, ms_time(NULL )); set_media_network_reachable(lc, lc->media_network_state.user_state && is_reachable); notify_network_reachable_change(lc); } }
在 linphone_core_set_network_reachable_internal
方法中首先判断是否开启了「开启了网络状态自动检测」配置,然后修改全局网络状态。
继续分析哪些调用方调用了 linphone_core_set_network_reachable_internal
方法:
1 2 3 4 5 6 void AndroidPlatformHelpers::setNetworkReachable (bool reachable) { mNetworkReachable = reachable; linphone_core_set_network_reachable_internal (getCore ()->getCCore (), reachable ? 1 : 0 ); }
继续分析哪些调用方调用了 AndroidPlatformHelpers::setNetworkReachable
方法:
1 2 3 4 5 6 7 8 9 extern "C" JNIEXPORT void JNICALL Java_org_linphone_core_tools_AndroidPlatformHelper_setNetworkReachable (JNIEnv* env, jobject thiz, jlong ptr, jboolean reachable) { AndroidPlatformHelpers *androidPlatformHelper = static_cast <AndroidPlatformHelpers *>((void *)ptr); const std::function<void ()> fun = [androidPlatformHelper, reachable]() { androidPlatformHelper->setNetworkReachable (reachable); }; androidPlatformHelper->getCore ()->doLater (fun); }
在这里创建了一个 fun
对象,类似 Java 中的 Runnable
接口,抛给 doLater
方法稍后执行。
跟踪到这里,追到了 JNI 层,看到了熟悉的 Java 包名和类名 AndroidPlatformHelper
,在 AS 中打开 AndroidPlatformHelper
,搜索调用 setNetworkReachable
的地方,共有 6 处,均在 updateNetworkReachability
方法中:
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public synchronized void updateNetworkReachability () { if (this .mNativePtr == 0L ) { Log.w(new Object []{"[Platform Helper] Native pointer has been reset, stopping there" }); } else if (this .mNetworkManager == null ) { Log.w(new Object []{"[Platform Helper] Network Manager is null, stopping there" }); } else { boolean connected = this .mNetworkManager.isCurrentlyConnected(this .mContext); if (!connected) { Log.i(new Object []{"[Platform Helper] No connectivity: setting network unreachable" }); this .setNetworkReachable(this .mNativePtr, false ); } else { if (this .mNetworkManager.hasHttpProxy(this .mContext)) { if (this .useSystemHttpProxy(this .mNativePtr)) { String host = this .mNetworkManager.getProxyHost(this .mContext); int port = this .mNetworkManager.getProxyPort(this .mContext); this .setHttpProxy(this .mNativePtr, host, port); if (!this .mUsingHttpProxy) { Log.i(new Object []{"[Platform Helper] Proxy wasn't set before, disabling network reachability first" }); this .setNetworkReachable(this .mNativePtr, false ); } this .mUsingHttpProxy = true ; } else { Log.w(new Object []{"[Platform Helper] Proxy available but forbidden by linphone core [sip] use_system_http_proxy setting" }); } } else { this .setHttpProxy(this .mNativePtr, "" , 0 ); if (this .mUsingHttpProxy) { Log.i(new Object []{"[Platform Helper] Proxy was set before, disabling network reachability first" }); this .setNetworkReachable(this .mNativePtr, false ); } this .mUsingHttpProxy = false ; } NetworkInfo networkInfo = this .mNetworkManager.getActiveNetworkInfo(); if (networkInfo == null ) { Log.e(new Object []{"[Platform Helper] getActiveNetworkInfo() returned null !" }); this .setNetworkReachable(this .mNativePtr, false ); } else { Log.i(new Object []{"[Platform Helper] Active network type is " + networkInfo.getTypeName() + ", state " + networkInfo.getState() + " / " + networkInfo.getDetailedState()}); if (networkInfo.getState() == State.DISCONNECTED && networkInfo.getDetailedState() == DetailedState.BLOCKED) { Log.w(new Object []{"[Platform Helper] Active network is in bad state..." }); } Network network = this .mNetworkManager.getActiveNetwork(); this .mNetworkManager.updateDnsServers(); int currentNetworkType = networkInfo.getType(); if (this .mLastNetworkType != -1 && this .mLastNetworkType != currentNetworkType) { Log.i(new Object []{"[Platform Helper] Network type has changed (last one was " + this .networkTypeToString(this .mLastNetworkType) + "), disabling network reachability first" }); this .setNetworkReachable(this .mNativePtr, false ); } this .mLastNetworkType = currentNetworkType; Log.i(new Object []{"[Platform Helper] Network reachability enabled" }); this .setNetworkReachable(this .mNativePtr, true ); } } } }
以上 6 处调用,只有最后的第 6 处传入了 True,认为网络可达;其余 5 处均传入了 False,认为网络不可达。上面的代码主要是获取当前活跃的网络信息,如果没有活跃的网络信息就认为网络不可达。
继续分析哪些调用方调用了 AndroidPlatformHelpers#updateNetworkReachability
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private synchronized void startNetworkMonitoring () { if (this .mMonitoringEnabled) { this .mNetworkManager = this .createNetworkManager(); Log.i(new Object []{"[Platform Helper] Registering network callbacks" }); this .mNetworkManager.registerNetworkCallbacks(this .mContext); this .updateNetworkReachability(); } }
startNetworkMonitoring
方法主要是开启网络状态自动检测功能,首先判断是否开启了自动检测网络状态配置,接下来根据 Android 系统版本创建网络管理器,然后注册网络变化回调,最后调用了 updateNetworkReachability
方法,进行一次网络状态通知。
前面提到笔者目前使用的设备是 API 25,在 createNetworkManager
方法中找到对应的网络管理器实现:
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public NetworkManagerAbove24 (AndroidPlatformHelper helper, ConnectivityManager cm, boolean wifiOnly) { this .mHelper = helper; this .mConnectivityManager = cm; this .mWifiOnly = wifiOnly; this .mNetworkAvailable = null ; this .mLastNetworkAvailable = null ; this .mNetworkCallback = new NetworkCallback () { public void onAvailable (final Network network) { NetworkManagerAbove24.this .mHelper.getHandler().post(new Runnable () { public void run () { NetworkInfo info = NetworkManagerAbove24.this .mConnectivityManager.getNetworkInfo(network); if (info == null ) { Log.e(new Object []{"[Platform Helper] [Network Manager 24] A network should be available but getNetworkInfo failed." }); } else { Log.i(new Object []{"[Platform Helper] [Network Manager 24] A network is available: " + info.getTypeName() + ", wifi only is " + (NetworkManagerAbove24.this .mWifiOnly ? "enabled" : "disabled" )}); if (NetworkManagerAbove24.this .mWifiOnly && info.getType() != 1 && info.getType() != 9 ) { Log.i(new Object []{"[Platform Helper] [Network Manager 24] Network isn't wifi and wifi only mode is enabled" }); if (NetworkManagerAbove24.this .mWifiOnly) { NetworkManagerAbove24.this .mLastNetworkAvailable = network; } } else { NetworkManagerAbove24.this .mNetworkAvailable = network; NetworkManagerAbove24.this .mHelper.updateNetworkReachability(); } } } }); } public void onLost (final Network network) { NetworkManagerAbove24.this .mHelper.getHandler().post(new Runnable () { public void run () { Log.i(new Object []{"[Platform Helper] [Network Manager 24] A network has been lost" }); if (NetworkManagerAbove24.this .mNetworkAvailable != null && NetworkManagerAbove24.this .mNetworkAvailable.equals(network)) { NetworkManagerAbove24.this .mNetworkAvailable = null ; } if (NetworkManagerAbove24.this .mLastNetworkAvailable != null && NetworkManagerAbove24.this .mLastNetworkAvailable.equals(network)) { NetworkManagerAbove24.this .mLastNetworkAvailable = null ; } NetworkManagerAbove24.this .mHelper.updateNetworkReachability(); } }); } }; } public void registerNetworkCallbacks (Context context) { int permissionGranted = context.getPackageManager().checkPermission("android.permission.ACCESS_NETWORK_STATE" , context.getPackageName()); Log.i(new Object []{"[Platform Helper] [Network Manager 24] ACCESS_NETWORK_STATE permission is " + (permissionGranted == 0 ? "granted" : "denied" )}); if (permissionGranted == 0 ) { this .mConnectivityManager.registerDefaultNetworkCallback(this .mNetworkCallback); } }
在 NetworkManagerAbove24.java
的实现中,网络可用时也会调用 AndroidPlatformHelper#updateNetworkReachability
方法。
到这里其实差不多了,因为分析下来,发现网络可用时通知 linphone 网络可达,网络丢失时通知 linphone 网络不可达,以上流程符合正常的思维逻辑,笔者追到这里,只能是怀疑在 startNetworkMonitoring
和 NetworkManagerAbove24#onAvailable
的两处调用有并发顺序问题,onAvailable
一定是在主线程执行的,而 startNetworkMonitoring
就不确定是在哪个线程执行了。
最后笔者想着关闭「自动检测网络状态」配置,由业务程序自身监听网络变化,然后调用 Core#setNetworkReachable
方法通知 linphone 网络是否可达。
auto_net_state_mon
在上一节,我们分析了 startNetworkMonitoring
方法,现在我们继续分析哪些调用方调用了 startNetworkMonitoring
方法:
1 2 3 4 5 6 7 public synchronized void onLinphoneCoreStart (boolean monitoringEnabled) { Log.i(new Object []{"[Platform Helper] onLinphoneCoreStart, network monitoring is " + monitoringEnabled}); this .mMonitoringEnabled = monitoringEnabled; this .startNetworkMonitoring(); }
onLinphoneCoreStart
方法在 AndroidPlatformHelper.java
中没有找到调用方,怀疑此方法是在 native 层调用了,我们去 android-platform-helpers.cpp
中搜索一番:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void AndroidPlatformHelpers::onLinphoneCoreStart (bool monitoringEnabled) { JNIEnv *env = ms_get_jni_env (); if (env) { if (mJavaHelper) { env->CallVoidMethod (mJavaHelper, mOnLinphoneCoreStartId, (jboolean)monitoringEnabled); } } }
在 AndroidPlatformHelpers::onLinphoneCoreStart
方法中调用了 AndroidPlatformHelper#onLinphoneCoreStart
方法,好的,我们继续分析 AndroidPlatformHelpers::onLinphoneCoreStart
的调用方,通过在 IDE 中搜索发现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 LinphoneStatus linphone_core_start (LinphoneCore *lc) { bool autoNetworkStateMonitoringEnabled = !!lc->auto_net_state_mon; if (!autoNetworkStateMonitoringEnabled) { bctbx_warning("Automatic network state monitoring is disabled by configuration (auto_net_state_mon=0). This is not recommended." ); bctbx_warning("In this mode, apps must use linphone_core_set_network_reachable() and linphone_core_set_dns_servers() to notify the LinphoneCore of network availability and provide the DNS server list." ); } getPlatformHelpers(lc)->onLinphoneCoreStart(autoNetworkStateMonitoringEnabled); }
bingo~,发现了 lc->auto_net_state_mon
,现在我们只要找到 lc->auto_net_state_mon
是在哪里赋值的就可以了,还是在 IDE 中搜索分析:
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 static void _linphone_core_read_config(LinphoneCore * lc) { sip_setup_register_all(lc->factory); sound_config_read(lc); net_config_read(lc); rtp_config_read(lc); codecs_config_read(lc); sip_config_read(lc); video_config_read(lc); misc_config_read(lc); ui_config_read(lc); #ifdef TUNNEL_ENABLED if (lc->tunnel) { linphone_tunnel_configure(lc->tunnel); } #endif lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; }
_linphone_core_read_config
方法主要是读取各种配置项,与 auto_net_state_mon
相关的主要是 sip_config_read(lc);
和 最后一行代码,最后一行代码使用 sip_conf
中的 auto_net_state_mon
为 lc->auto_net_state_mon
赋值,sip_conf
在 sip_config_read(lc);
中赋值:
1 2 3 4 5 6 7 8 9 static void sip_config_read (LinphoneCore *lc) { lc->sip_conf.auto_net_state_mon = !!linphone_config_get_int(lc->config,"sip" ,"auto_net_state_mon" ,1 ); }
在 sip_config_read
方法中,通过读取 Config
中的 Section
和 Key
赋值,如果没有相应的 Section
和 Key
,则取默认值 1。
终于找到配置「自动检测网络状态」开关的地方了,Config
是在 Java 层创建 Core
时传入的,所以现在我们只需修改 Java 层代码即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int start () { Config configWithFactory = mFactory.createConfigWithFactory(null , null ); if (mConfig.autoNetStateMonitorEnabled()) { configWithFactory.setInt("sip" , "auto_net_state_mon" , 1 ); } else { configWithFactory.setInt("sip" , "auto_net_state_mon" , 0 ); } mCore = mFactory.createCoreWithConfig(configWithFactory, mContext.getApplicationContext()); }
首先通过 Factory
创建空的 Config
对象,然后根据外部传入的配置判断是否开启linphone的「自动检测网络状态」开关,随后根据 Config
对象创建 Core
,
最后运行程序查看 Logcat 日志,在 AndroidPlatformHelper#onLinphoneCoreStart
方法中有输出是否开启了「自动检测网络状态」:
1 linphone-android : [Platform Helper] onLinphoneCoreStart, network monitoring is false
happy~
总结
本文笔者记录了从根据问题现象,分析问题,反向跟踪源码到网络检测逻辑,至网络检测逻辑时,问题分析陷入困境,再到大胆假设是并发引起的问题,随后沿着并发引起问题的假设,想到关闭 linphone「自动检测网络状态」配置的方法,最后分析「自动检测网络状态」配置的开启流程。
笔者在分析排查期间有一步放弃的话,也看不到最后的曙光。
不经历风雨怎能见彩虹,希望可以帮到你~