1. 信号强度算法
WifiManager.Java
private static final int MIN_RSSI = -100; private static final int MAX_RSSI = -55; public static int calculateSignalLevel( int rssi, int numLevels) { if (rssi <= MIN_RSSI) { return 0; } else if (rssi >= MAX_RSSI) { return numLevels - 1; } else { float inputRange = (MAX_RSSI - MIN_RSSI); float outputRange = (numLevels - 1); return ( int )(( float )(rssi - MIN_RSSI) * outputRange / inputRange); } }
2. WiFi Command流程
3. wpa_supplicant启动流程
4. WifiService启动流程
5. SIGNAL_POLL调用流程
eloop_run->.. wpa_supplicant_ctrl_iface_receive-> wpa_supplicant_ctrl_iface_process-> (SIGNAL_POLL) wpa_supplicant_signal_poll-> wpa_drv_signal_poll (struct wpa_supplicant *wpa_s, struct wpa_signal_info *si)-> wpa_driver_signal_poll (void *priv, struct wpa_signal_info *si)-> wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof (buf))或 wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof (buf))-> struct iwreq iwr; iwr.u.data.pointer = buf; iwr.u.data.length = buf_len; ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); 在Kernel中对应函数: cfg80211_wext_setpriv (wext-compat.c) RSSI_CMD: cfg80211_wireless_stats (获取当前已连接AP的信号强度等信息) 对于上面的LINKSPEED_CMD,如果ioctl不成功,则调用ioctl(drv->ioctl_sock, SIOCGIWRATE, &wrq) 在Kernel中对应函数: cfg80211_wext_giwrate (获取当前已连接AP的发送速度) struct station_info { u32 filled; u32 connected_time; u32 inactive_time; u32 rx_bytes; u32 tx_bytes; u16 llid; u16 plid; u8 plink_state; s8 signal; s8 signal_avg; struct rate_info txrate; struct rate_info rxrate; u32 rx_packets; u32 tx_packets; u32 tx_retries; u32 tx_failed; u32 rx_dropped_misc; struct sta_bss_parameters bss_param; int generation; };
接下来介绍网络可用性对评分的影响。
该影响主要体现在,当一个网络连接建立时,系统将用该连接Ping一个Google的网站来判断该连接是否真的可以上网,如果不可以,那么就会扣掉该网络40分,从而可能导致该网络的评分低于其他网络评分 ,下面来看详细过程。
一、NetworkMonitor来源
在前面《
网络连接评分机制之NetworkAgent
》我们分析过,当某个NetworkFactory连接上网络时,就会创建NetworkAgent对象,然后注册到ConnectivityService,而在注册过程中,ConnectivityService将会利用NetworkAgent传递过来的NetworkInfo、Messenger、分值等信息创建NetworkAgentInfo对象。而在该对象的创建过程中,将会创建一个网络监听器NetworkMonitor,下面来看这个过程:
@ConnectivityService .java public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkMisc networkMisc) { NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new NetworkInfo(networkInfo), new LinkProperties(linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc)); synchronized ( this ) { nai.networkMonitor.systemReady = mSystemReady; } mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); }
这就是我们所说的注册NetworkAgent时所创建的NetworkAgentInfo对象,然后来看该对象的属性:
public class NetworkAgentInfo {}
再来看他提供的方法:
public void addRequest(NetworkRequest networkRequest) {} public int getCurrentScore() {} public void setCurrentScore( int newScore) {} public String toString() {} public String name() {}
然后来看NetworkAgentInfo创建过程:
@NetworkAgentInfo .java public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc) { this .messenger = messenger; asyncChannel = ac; network = null ; networkInfo = info; linkProperties = lp; networkCapabilities = nc; currentScore = score; networkMonitor = new NetworkMonitor(context, handler, this ); networkMisc = misc; created = false ; validated = false ; }
从这些信息我们看到,NetworkAgentInfo没有继承其他类,同时也只是提供了一些设置或者查询当前对象属性的一些方法,该对象的
主要作用也就是保存各个向ConnectivityService注册的NetworkAgent,以便于查询或修改某个NetworkAgent对象的相关信息
。
但是从NetworkAgentInfo的构造方法中我们看到他创建了一个NetworkMonitor对象,那么该对象的作用是什么呢?
二、NetworkMonitor作用与初始化流程
NetworkMonitor是ConnectivityService用于管理网络连接状态而创建的状态机,主要作用就是检测当前网络的有效性。
下面我们来看NetworkMonitor的属性与初始化流程:
@NetworkMonitor .java public class NetworkMonitor extends StateMachine {} public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) { super (TAG + networkAgentInfo.name()); mContext = context; mConnectivityServiceHandler = handler; mNetworkAgentInfo = networkAgentInfo; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); addState(mDefaultState); addState(mOfflineState, mDefaultState); addState(mValidatedState, mDefaultState); addState(mEvaluatingState, mDefaultState); addState(mUserPromptedState, mDefaultState); addState(mCaptivePortalState, mDefaultState); addState(mLingeringState, mDefaultState); setInitialState(mDefaultState); mServer = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_SERVER); if (mServer == null ) mServer = DEFAULT_SERVER; mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY, DEFAULT_REEVALUATE_DELAY_MS); mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1 ) == 1 ; start(); }
从这个状态机的初始化流程中我们可以看到两个信息:1、该类内部有七个状态机;2、初始化的状态机是DefaultState 。接下来分别介绍各个状态机的作用和状态机运作流程。
三、NetworkMonitor各个状态机作用
NetworkMonitor共有七个状态机,分别是:DefaultState、OfflineState、ValidatedState、EvaluatingState、UserPromptedState、CaptivePortalState、LingeringState。他们的作用是:
DefaultState
----这是默认的状态机,也是其他所有状态机的父状态,主要处理网络连接的主要状态变化,包括连接、断开、测试、延时等模式。
EvaluatingState
----验证状态,网络连上时,将会进入该状态,然后会Ping网络,判断当前网络有效性,并决定下一步是进入ValidatedState还是OfflineState或者UserPromptedState。
OfflineState
----脱机状态,当Ping网络时,始终没有任何回应时,就会进入该状态。
ValidatedState
----验证通过的状态,当Ping通网络时,说明当前的网络是通畅的,将会进入该状态。
UserPromptedState
----验证失败状态,当Ping网络时,网络给出了重定向异常,比如接入中国移动时会跳入移动的帐号认证页面,需要用户进行网络登录后才可以继续上网。此时一般需要在界面上提示用户。
CaptivePortalState
----当网络被测试 失败时进入UserPromptedState后,用户可以通过发送ACTION_SIGN_IN_REQUESTED的消息来进入CaptivePortalState状态,该状态中将会监听ACTION_CAPTIVE_PORTAL_LOGGED_IN消息,并可直接由该消息指定进入ValidatedState或者OfflineState模式。
四、NetworkMonitor对网络评分的影响
下面我们从一次正常网络接入过程来看NetworkMonitor对评分的影响。
从NetworkMonitor初始化过程我们知道,该状态机的默认状态是DefaultState,并且从NetworkAgent向ConnectivityService注册过程可以看到,在NetworkAgent注册过程中最后将会通过updateNetworkInfo方法更新当前网络状态为CONNECTED:
@ConnectivityService .java private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) { if (state == NetworkInfo.State.CONNECTED && !networkAgent.created) { networkAgent.created = true ; updateLinkProperties(networkAgent, null ); notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK); networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); rematchNetworkAndRequests(networkAgent, false ); } else if (state == NetworkInfo.State.DISCONNECTED || state == NetworkInfo.State.SUSPENDED) { } }
这里我们发现,更新状态的同时,向NetworkMonitor发送了CMD_NETWORK_CONNECTED的消息,根据NetworkMonitor的状态机原理,此消息将在NetworkMonitor中的DefaultState状态机中来处理:
@NetworkMonitor .java private class DefaultState extends State { public boolean processMessage(Message message) { switch (message.what) { case CMD_NETWORK_CONNECTED: transitionTo(mEvaluatingState); return HANDLED; } } }
从这里我们看到,此时的NetworkMonitor将会进入EvaluatingState的状态中,然后我们来看该状态的初始化过程:
private class EvaluatingState extends State { private int mRetries; public void enter() { mRetries = 0 ; sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0 ); if (mUidResponsibleForReeval != INVALID_UID) { TrafficStats.setThreadStatsUid(mUidResponsibleForReeval); mUidResponsibleForReeval = INVALID_UID; } } public boolean processMessage(Message message) { switch (message.what) { case CMD_REEVALUATE: if (message.arg1 != mReevaluateToken) return HANDLED; if (mNetworkAgentInfo.isVPN()) { transitionTo(mValidatedState); return HANDLED; } int httpResponseCode = isCaptivePortal(); if (httpResponseCode == 204 ) { transitionTo(mValidatedState); } else if (httpResponseCode >= 200 && httpResponseCode <= 399 ) { transitionTo(mUserPromptedState); } else if (++mRetries > MAX_RETRIES) { transitionTo(mOfflineState); } else if (mReevaluateDelayMs >= 0 ) { Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0 ); sendMessageDelayed(msg, mReevaluateDelayMs); } return HANDLED; case CMD_FORCE_REEVALUATION: return HANDLED; default : return NOT_HANDLED; } } }
从这个状态机的初始化过程我们发现,进入该状态时,将会发送一个CMD_REEVALUATE的消息,然后在该状态机内部收到该消息时,就会通过isCaptivePortal方法来Ping网络:
private int isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) return 204 ; HttpURLConnection urlConnection = null ; int httpResponseCode = 599 ; try { URL url = new URL( "http" , mServer, "/generate_204" ); urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url); urlConnection.setInstanceFollowRedirects(false ); urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); urlConnection.setUseCaches(false ); long requestTimestamp = SystemClock.elapsedRealtime(); urlConnection.getInputStream(); long responseTimestamp = SystemClock.elapsedRealtime(); httpResponseCode = urlConnection.getResponseCode(); if (httpResponseCode == 200 && urlConnection.getContentLength() == 0 ) { httpResponseCode = 204 ; } sendNetworkConditionsBroadcast(true , httpResponseCode == 204 , requestTimestamp, responseTimestamp); } catch (IOException e) { if (httpResponseCode == 599 ) { } } finally { if (urlConnection != null ) { urlConnection.disconnect(); } } return httpResponseCode; }
从这里我们看到,其向一个url网址进行Ping操作,而这个url的具体内容为:
URL url = new URL( "http" , mServer, "/generate_204" );
而这里的mServer的默认值是从NetworkMonitor初始化时获取的:
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) { mServer = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_SERVER); if (mServer == null ) mServer = DEFAULT_SERVER; }
而DEFAULT_SERVER内容为:
private static final String DEFAULT_SERVER = "clients3.google.com" ;
这就说明,这个url的具体地址为:
“http://clients3.google.com/generate_204”
,然后当isCaptivePortal结束之后,EvaluatingState就会对结果进行分类:
如果httpResponseCode=204
,就说明当前网络是可用的,将会进入ValidatedState状态。
如果httpResponseCode >= 200同时httpResponseCode <= 399
,说明当前网络需要进行重定向,当认证完成后才可以有效访问网络。
如果没有任何回应
,将会通过sendMessageDelayed方式延时mReevaluateDelayMs(默认为5秒)之后再次发送CMD_REEVALUATE消息,触发循环Ping过程,直到尝试次数超过MAX_RETRIES(10次)之后,进入OfflineState状态,表示当前网络无法使用。
那么当当前网络可用或者不可用时,对NetworkAgent评分有什么影响呢?
我们来看当该Ping通过后,进入ValidatedState状态时将会触发什么动作:
private class ValidatedState extends State { @Override public void enter() { mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_VALID, 0 , mNetworkAgentInfo)); } @Override public boolean processMessage(Message message) { switch (message.what) { case CMD_NETWORK_CONNECTED: transitionTo(mValidatedState); return HANDLED; default : return NOT_HANDLED; } } }
这个状态机比较简单,在进入该状态机时,只是向ConnectivityService发送了一条EVENT_NETWORK_TESTED的消息,并携带了当前网络的NetworkAgentInfo对象和测试通过(NETWORK_TEST_RESULT_VALID)的参数。
然后我们来看,当Ping不通过时,发生重定向时进入的UserPromptedState状态机会是怎样的处理:
private class UserPromptedState extends State { private static final String ACTION_SIGN_IN_REQUESTED = "android.net.netmon.sign_in_requested" ; private CustomIntentReceiver mUserRespondedBroadcastReceiver; @Override public void enter() { mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0 , mNetworkAgentInfo)); mUserRespondedBroadcastReceiver = new CustomIntentReceiver(ACTION_SIGN_IN_REQUESTED, ++mUserPromptedToken, CMD_USER_WANTS_SIGN_IN); Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1 , mNetworkAgentInfo.network.netId, mUserRespondedBroadcastReceiver.getPendingIntent()); mConnectivityServiceHandler.sendMessage(message); } @Override public boolean processMessage(Message message) { switch (message.what) { case CMD_USER_WANTS_SIGN_IN: if (message.arg1 != mUserPromptedToken) return HANDLED; transitionTo(mCaptivePortalState); return HANDLED; default : return NOT_HANDLED; } } }
这个状态机与ValidatedState类似,也是在进入时向ConnectivityService发送了同样的EVENT_NETWORK_TESTED消息,并携带当前的NetworkAgentInfo对象,不同的是,该消息中携带了一个测试不通过的参数(NETWORK_TEST_RESULT_INVALID)。
下面我们来看ConnectivityService接收到该消息后的处理:
@ConnectivityService .java private class NetworkStateTrackerHandler extends Handler { public void handleMessage(Message msg) { NetworkInfo info; switch (msg.what) { case NetworkMonitor.EVENT_NETWORK_TESTED: { NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj; if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED" )) { boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); if (valid) { final boolean previouslyValidated = nai.validated; final int previousScore = nai.getCurrentScore(); nai.validated = true ; rematchNetworkAndRequests(nai, !previouslyValidated); if (nai.getCurrentScore() != previousScore) { sendUpdatedScoreToFactories(nai); } } updateInetCondition(nai, valid); nai.asyncChannel.sendMessage( android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS, (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), 0 , null ); } break ; } } } }
在这里我们看到,
通过与不通过的差距就在于是否将NetworkAgentInfo的validated标志位设置为true
。
那么为什么这个标志位会对评分产生影响呢?我们来看NetworkAgentInfo中关于该标志位的使用:
当NetworkAgent注册到ConnectivityService时,将会创建该NetworkAgent的NetworkAgentInfo,此时该标志位的默认状态是false:
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc) { this .messenger = messenger; asyncChannel = ac; network = null ; networkInfo = info; linkProperties = lp; networkCapabilities = nc; currentScore = score; networkMonitor = new NetworkMonitor(context, handler, this ); networkMisc = misc; created = false ; validated = false ; }
这也很容易理解,因为此时说明网络连接建立完成,但是对于ConnectivityService来说,并不能确定该网络完全可用,因此默认状态下网络都是未经检验的。
而当Ping通过之后,说明该网络通过了可用性检测,那么此时在对该标志位赋值也是合理的。
而该标志位直接影响到NetworkAgentInfo中该网络的分值计算:
public int getCurrentScore() { int score = currentScore; if (!validated) score -= UNVALIDATED_SCORE_PENALTY; if (score < 0 ) score = 0 ; if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE; return score; }
从这里我们终于找到了Ping网络对评分的约束:无论是通过rematchNetworkAndRequests重新匹配最佳NetworkFactory,还是通过sendUpdatedScoreToFactories广播最新网络的分值,都是需要从NetworkAgentInfo中通过getCurrentScore获取最新分值的,
而如果当前网络没有经过有效性检测,那么对其他所有的NetworkFactory来说,当前的网络分值都是被扣掉40分之后的分值
。
这就完全可能让原本分值高的网络由于没有通过有效性检测,而低于其他网络优先级。
比如WIFI默认60分,而数据连接默认50分,但是如果WIFI没有通过有效性检测,那么对数据连接来说,WIFI的分数只有60-40=20分,由此就会引发WIFI连接时,数据也在连接的异常。
这就是网络可用性对评分的影响。
用户原本在用数据上网,但是如果到了一个有WIFI的环境,并连接上了WIFI,此时用户的手机将会自动断开数据网络,这是如何做到的呢?
当用户来到WIFI环境时,如果连上了某个WIFI,那么此时的WIFI状态机将会进入L2ConnectedState状态,然后会更新当前NetworkInfo状态为CONNECTING状态,并用该NetworkInfo创建WIFI的NetworkAgent对象,同时标明当前WIFI分值为60。即:
mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext, "WifiNetworkAgent" , mNetworkInfo, mNetworkCapabilitiesFilter, mLinkProperties, 60 );
然后在NetworkAgent初始化过程中将会向ConnectivityService注册,然后ConnectivityService就会创建WIFI的NetworkAgentInfo对象:
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new NetworkInfo(networkInfo), new LinkProperties(linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc));
然后ConnectivityService将会在handleRegisterNetworkAgent方法中更新当前WIFI的NetworkAgentInfo为CONNECTED状态,并向NetworkMonitor发送连接成功的消息,然后经过有效性检测,
WIFI的分值将会保持在60分
。
然后在rematchNetworkAndRequests中,将发现现有的网络请求所使用的数据流量方案的分值(50分)低于WIFI的分值,将会把最新WIFI的分值通过sendUpdatedScoreToFactories发送到各个NetworkFactory中,其中就包括数据网络:
private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) { for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0 , networkRequest); } }
此时数据网络会再次进行分值对比,并
发现自己的分值的确低于目标WIFI分值
:
private void evalRequest(NetworkRequestInfo n) { if (n.requested == false && n.score < mScore && n.request.networkCapabilities.satisfiedByNetworkCapabilities( mCapabilityFilter) && acceptRequest(n.request, n.score)) { needNetworkFor(n.request, n.score); n.requested = true ; } else if (n.requested == true && (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false )) { releaseNetworkFor(n.request); n.requested = false ; } }
然后
数据网络就进行releaseNetworkFor操作
:
@DcTracker .java protected void releaseNetworkFor(NetworkRequest networkRequest) { ApnContext apnContext = apnContextForNetworkRequest(networkRequest); if (apnContext != null ) apnContext.decRefCount(); }
而
APN参数被删掉时将会触发数据业务的关闭
:
@ApnContext .java public void decRefCount() { synchronized (mRefCountLock) { if (mRefCount-- == 1 ) { mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false ); } } }
然后数据业务就被关闭,从而完成网络切换的任务。
WIFI 衡量接收信号强度可以用直接的 RF 能量 dBm 来表示,也可以用 RSSI 这个相对值。得到的值是一个0到-100的区间值,是一个int型数据,其中0到-50表示信号最好,-50到-70表示信号偏差,小于-70表示最差,有可能连接不上或者掉线。
下图中 WIFI 信号强度为 -65dBm ,一般 -60~-70 算是信号很好。
Received signal strength indicator (RSSI) 是 WIFI 衡量接收信号强度的一个相对值。
说白了就是让大家好理解信号强度,不然都用 dBm 谁知道多少 dBm 算信号强啊。
如 :
RSSI = 0 时,我们 PC WIFI 那个 Bar 可能信号只有一格或一格都没有,我们说信号很差,其可能对应实际能量 < -90dBm 。
RSSI=120~127 时, WIFI Bar 可能信号满格,其可能对应实际能量 > -60dBm 等。
RSSI 值的定义由 WIFI 芯片厂家自己定,如一个厂家可以定义 RSSI = 80 对应 -65dBm ;另一个可以定义 RSSI = 80 对应 -60dBm 等。 Qualcomm Atheros 的 RSSI 一般是 0-127 。
所以,对于相同信号强度,实际的 RSSI 值可能不同,对于用户还是看有几格信号最直接和省事。
用户原本在用数据上网,但是如果到了一个有WIFI的环境,并连接上了WIFI,此时用户的手机将会自动断开数据网络,这是如何做到的呢?
当用户来到WIFI环境时,如果连上了某个WIFI,那么此时的WIFI状态机将会进入L2ConnectedState状态,然后会更新当前NetworkInfo状态为CONNECTING状态,并用该NetworkInfo创建WIFI的NetworkAgent对象,同时标明当前WIFI分值为60。即:
mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext, "WifiNetworkAgent" , mNetworkInfo, mNetworkCapabilitiesFilter, mLinkProperties, 60 );
然后在NetworkAgent初始化过程中将会向ConnectivityService注册,然后ConnectivityService就会创建WIFI的NetworkAgentInfo对象:
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new NetworkInfo(networkInfo), new LinkProperties(linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc));
然后ConnectivityService将会在handleRegisterNetworkAgent方法中更新当前WIFI的NetworkAgentInfo为CONNECTED状态,并向NetworkMonitor发送连接成功的消息,然后经过有效性检测,
WIFI的分值将会保持在60分
。
然后在rematchNetworkAndRequests中,将发现现有的网络请求所使用的数据流量方案的分值(50分)低于WIFI的分值,将会把最新WIFI的分值通过sendUpdatedScoreToFactories发送到各个NetworkFactory中,其中就包括数据网络:
private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) { for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0 , networkRequest); } }
此时数据网络会再次进行分值对比,并
发现自己的分值的确低于目标WIFI分值
:
private void evalRequest(NetworkRequestInfo n) { if (n.requested == false && n.score < mScore && n.request.networkCapabilities.satisfiedByNetworkCapabilities( mCapabilityFilter) && acceptRequest(n.request, n.score)) { needNetworkFor(n.request, n.score); n.requested = true ; } else if (n.requested == true && (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false )) { releaseNetworkFor(n.request); n.requested = false ; } }
然后
数据网络就进行releaseNetworkFor操作
:
@DcTracker .java protected void releaseNetworkFor(NetworkRequest networkRequest) { ApnContext apnContext = apnContextForNetworkRequest(networkRequest); if (apnContext != null ) apnContext.decRefCount(); }
而
APN参数被删掉时将会触发数据业务的关闭
:
@ApnContext .java public void decRefCount() { synchronized (mRefCountLock) { if (mRefCount-- == 1 ) { mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false ); } } }
然后数据业务就被关闭,从而完成网络切换的任务。