该影响主要体现在,当一个网络连接建立时,系统将用该连接Ping一个Google的网站来判断该连接是否真的可以上网,如果不可以,那么就会扣掉该网络40分,从而可能导致该网络的评分低于其他网络评分,下面来看详细过程。
@ConnectivityService.java
public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkMisc networkMisc) {
//注册NetworkAgent时需要创建NetworkAgentInfo
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
networkMonitor = new NetworkMonitor(context, handler, this);
networkMisc = misc;
created = false;
validated = false;
}
从这些信息我们看到,NetworkAgentInfo没有继承其他类,同时也只是提供了一些设置或者查询当前对象属性的一些方法,该对象的
主要作用也就是保存各个向ConnectivityService注册的NetworkAgent,以便于查询或修改某个NetworkAgent对象的相关信息。
但是从NetworkAgentInfo的构造方法中我们看到他创建了一个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);
//初始状态机为DefaultState
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。接下来分别介绍各个状态机的作用和状态机运作流程。
----当网络被测试失败时进入UserPromptedState后,用户可以通过发送ACTION_SIGN_IN_REQUESTED的消息来进入CaptivePortalState状态,该状态中将会监听ACTION_CAPTIVE_PORTAL_LOGGED_IN消息,并可直接由该消息指定进入ValidatedState或者OfflineState模式。
@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);
// Consider network even though it is not yet validated.
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;
}
//Ping网络
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 {
//准备连接的uri
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 /* response received */, 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就会对结果进行分类:
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)的参数。
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.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();
//标记NetworkAgentInfo的validated状态为true
nai.validated = true;
rematchNetworkAndRequests(nai, !previouslyValidated);
if (nai.getCurrentScore() != previousScore) {
//将最新分数更新到其他NetworkFactory
sendUpdatedScoreToFactories(nai);
}
}
updateInetCondition(nai, valid);
//通知NetworkAgent
nai.asyncChannel.sendMessage( android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS, (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), 0, null);
}
break;
}
}
}
}
在这里我们看到,
通过与不通过的差距就在于是否将NetworkAgentInfo的validated标志位设置为true。
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;
//默认值为false
validated = false;
}
这也很容易理解,因为此时说明网络连接建立完成,但是对于ConnectivityService来说,并不能确定该网络完全可用,因此默认状态下网络都是未经检验的。
public int getCurrentScore() {
//默认是NetworkAgent带过来的
int score = currentScore;
//如果没有通过Ping的检测,那么该网络就要被扣掉UNVALIDATED_SCORE_PENALTY=40分
if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
//有效性保护
if (score < 0) score = 0;
//如果该网络是用户特意指定的,分值就是EXPLICITLY_SELECTED_NETWORK_SCORE=100
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连接时,数据也在连接的异常。
这就是网络可用性对评分的影响。