linphone-sdk-android网络检测分析
原文 linphone-sdk-android网络检测分析 - 掘金 (juejin.cn)
前言
好久没写 linphone-sdk-android
相关的文章了,上一篇文章还是一个月之前,经过上次修改 linphone-sdk-android
后最近没有啥问题发生,本文记录下之前遇到的 linphone
网络问题的坑。
注:笔者的 App 作为 Launcher 启动,目前运行在 Android 7.1.2 API 25 系统上,使用以太网连接。
分析
注:以下源码基于 linphone-sdk-android 4.5.26。
问题描述:在设备重启后,程序偶现发起不了呼叫,提示当前网络不可达,但是当前网络是好的,这就比较奇怪了。
接下来查看设备重启后的日志,发现一些与 linphone 网络相关的:
liblinphone : SIP network reachability state is now DOWN
liblinphone : SIP network reachability state is now UP
liblinphone : SIP network reachability state is now DOWN
刷新账户注册时也提示网络不可达,日志如下:
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
关键字:
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
,我们跟进去看看:
// linphonecore.c
void linphone_core_set_network_reachable(LinphoneCore *lc, bool_t is_reachable) {
bool_t reachable = is_reachable;
// SIP协议层网络状态 - 用户设置的状态
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();
// 设置SIP协议层网络状态 - 全局状态
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
方法:
// linphonecore.c
static void set_sip_network_reachable(LinphoneCore* lc,bool_t is_sip_reachable, time_t curtime){
// second get the list of available proxies
const bctbx_list_t *elem = NULL;
// SIP协议层网络状态 - 全局状态,比较是否有变化
if (lc->sip_network_state.global_state==is_sip_reachable) return; // no change, ignore.
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;
// 修改SIP协议层网络状态 - 全局状态
lc->sip_network_state.global_state=is_sip_reachable;
// ......
}
在 set_sip_network_reachable
方法中,首先判断传入的值是否与已有的值相等,相等则认为没有变化,忽略此次调用,然后打印传入 is_sip_reachable
的值,这行日志对应前面提到的设备重启后与网络相关的日志,最后修改「SIP协议层网络状态 - 全局状态」。
前面提到刷新账户注册时也提示网络不可达,赶巧了,set_sip_network_reachable
方法的下面就是调用刷新账户注册的实现:
// linphonecore.c
void linphone_core_refresh_registers(LinphoneCore* lc) {
// 判断SIP协议层网络状态 - 全局状态
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
的实现:
// linphonecore.c
bool_t linphone_core_is_network_reachable(LinphoneCore* lc) {
// 返回SIP协议层网络状态 - 全局状态
return lc->sip_network_state.global_state;
}
在 Java 中调用 isNetworkReachable
最终会调用到 linphone_core_is_network_reachable
方法,在 linphone_core_is_network_reachable
方法中,直接返回了「SIP协议层网络状态 - 全局状态」。
AndroidPlatformHelper#setNetworkReachable
在上一节中我们分析是在 set_sip_network_reachable
方法修改的「SIP协议层网络状态 - 全局状态」,除了我们调用 Core#setNetworkReachable
外,应该还有调用方,在 IDE 中分析发现了 linphone_core_set_network_reachable_internal
方法:
// linphonecore.c
void linphone_core_set_network_reachable_internal(LinphoneCore *lc, bool_t is_reachable) {
// 如果开启了网络状态自动检测
if (lc->auto_net_state_mon) {
// 设置SIP协议层网络状态 - 全局状态
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
方法:
// android-platform-helpers.cpp
void AndroidPlatformHelpers::setNetworkReachable(bool reachable) {
mNetworkReachable = reachable;
linphone_core_set_network_reachable_internal(getCore()->getCCore(), reachable ? 1 : 0);
}
继续分析哪些调用方调用了 AndroidPlatformHelpers::setNetworkReachable
方法:
// android-platform-helpers.cpp
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
方法中:
// AndroidPlatformHelper.java
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
方法:
// AndroidPlatformHelper.java
private synchronized void startNetworkMonitoring() {
// 判断是否开启了自动检测网络状态
if (this.mMonitoringEnabled) {
// 根据 Android 系统版本创建网络管理器
this.mNetworkManager = this.createNetworkManager();
Log.i(new Object[]{"[Platform Helper] Registering network callbacks"});
// 注册网络变化回调
this.mNetworkManager.registerNetworkCallbacks(this.mContext);
// ......
// 调用了updateNetworkReachability
this.updateNetworkReachability();
}
}
startNetworkMonitoring
方法主要是开启网络状态自动检测功能,首先判断是否开启了自动检测网络状态配置,接下来根据 Android 系统版本创建网络管理器,然后注册网络变化回调,最后调用了 updateNetworkReachability
方法,进行一次网络状态通知。
前面提到笔者目前使用的设备是 API 25,在 createNetworkManager
方法中找到对应的网络管理器实现:
// NetworkManagerAbove24.java
// ......
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) {
// MainHandler
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;
// 调用了updateNetworkReachability
NetworkManagerAbove24.this.mHelper.updateNetworkReachability();
}
}
}
});
}
// 网络丢失
public void onLost(final Network network) {
// MainHandler
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;
}
// 调用了updateNetworkReachability
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
方法:
// AndroidPlatformHelper.java
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
中搜索一番:
// android-platform-helpers.cpp
void AndroidPlatformHelpers::onLinphoneCoreStart(bool monitoringEnabled) {
JNIEnv *env = ms_get_jni_env();
if (env) {
// ......
// 调用AndroidPlatformHelper#onLinphoneCoreStart
if (mJavaHelper) {
env->CallVoidMethod(mJavaHelper, mOnLinphoneCoreStartId, (jboolean)monitoringEnabled);
}
}
}
在 AndroidPlatformHelpers::onLinphoneCoreStart
方法中调用了 AndroidPlatformHelper#onLinphoneCoreStart
方法,好的,我们继续分析 AndroidPlatformHelpers::onLinphoneCoreStart
的调用方,通过在 IDE 中搜索发现:
// linphonecore.c
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.");
}
// 调用AndroidPlatformHelpers::onLinphoneCoreStart
getPlatformHelpers(lc)->onLinphoneCoreStart(autoNetworkStateMonitoringEnabled);
// ......
}
bingo~,发现了 lc->auto_net_state_mon
,现在我们只要找到 lc->auto_net_state_mon
是在哪里赋值的就可以了,还是在 IDE 中搜索分析:
// linphonecore.c
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
sip_config_read(lc);
video_config_read(lc);
//autoreplier_config_init(&lc->autoreplier_conf);
misc_config_read(lc);
ui_config_read(lc);
#ifdef TUNNEL_ENABLED
if (lc->tunnel) {
linphone_tunnel_configure(lc->tunnel);
}
#endif
// 使用sip_conf->auto_net_state_mon赋值
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);
中赋值:
// linphonecore.c
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 层代码即可:
public int start() {
// 创建Config
Config configWithFactory = mFactory.createConfigWithFactory(null, null);
// 根据外部传入的配置判断是否开启linphone的「自动检测网络状态」开关
if (mConfig.autoNetStateMonitorEnabled()) {
configWithFactory.setInt("sip", "auto_net_state_mon", 1);
} else {
configWithFactory.setInt("sip", "auto_net_state_mon", 0);
}
// 根据Config创建Core
mCore = mFactory.createCoreWithConfig(configWithFactory, mContext.getApplicationContext());
}
首先通过 Factory
创建空的 Config
对象,然后根据外部传入的配置判断是否开启linphone的「自动检测网络状态」开关,随后根据 Config
对象创建 Core
,
最后运行程序查看 Logcat 日志,在 AndroidPlatformHelper#onLinphoneCoreStart
方法中有输出是否开启了「自动检测网络状态」:
linphone-android : [Platform Helper] onLinphoneCoreStart, network monitoring is false
happy~
总结
本文笔者记录了从根据问题现象,分析问题,反向跟踪源码到网络检测逻辑,至网络检测逻辑时,问题分析陷入困境,再到大胆假设是并发引起的问题,随后沿着并发引起问题的假设,想到关闭 linphone「自动检测网络状态」配置的方法,最后分析「自动检测网络状态」配置的开启流程。
笔者在分析排查期间有一步放弃的话,也看不到最后的曙光。
不经历风雨怎能见彩虹,希望可以帮到你~