目录
1.流程梳理
1.1 网络校验
1.1.1 网络校验失败
1.1.2 网络校验成功
1.2 超时校验
2.总结
ConnectivityService
case EVENT_NETWORK_TESTED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
final boolean wasPartial = nai.partialConnectivity;
nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
final boolean partialConnectivityChanged =
(wasPartial != nai.partialConnectivity);
final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai);
// Only show a connected notification if the network is pending validation
// after the captive portal app was open, and it has now validated.
if (nai.captivePortalValidationPending && valid) {
// User is now logged in, network validated.
nai.captivePortalValidationPending = false;
showNetworkNotification(nai, NotificationType.LOGGED_IN);
}
final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
if (DBG) {
final String logMsg = !TextUtils.isEmpty(redirectUrl)
? " with redirect to " + redirectUrl
: "";
log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
if (valid != nai.lastValidated) {
if (wasDefault) {
metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
SystemClock.elapsedRealtime(), valid);
}
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
nai.everValidated |= valid;
updateCapabilities(oldScore, nai, nai.networkCapabilities);
// If score has changed, rebroadcast to NetworkFactories. b/17726566
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
// Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
// notifications if network becomes valid.
mNotifier.clearNotification(nai.network.netId,
NotificationType.NO_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.LOST_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.PARTIAL_CONNECTIVITY);
}
} else if (partialConnectivityChanged) {
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
Bundle redirectUrlBundle = new Bundle();
redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
// TODO: Evaluate to update partial connectivity to status to NetworkAgent.
nai.asyncChannel.sendMessage(
NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, redirectUrlBundle);
// If NetworkMonitor detects partial connectivity before
// EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
// immediately. Re-notify partial connectivity silently if no internet
// notification already there.
if (!wasPartial && nai.partialConnectivity) {
// Remove delayed message if there is a pending message.
mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
handlePromptUnvalidated(nai.network);
}
if (wasValidated && !nai.lastValidated) {
handleNetworkUnvalidated(nai);
}
break;
}
这边对WiFi的影响主要看,至于评分机制导致网络重新评分的之前梳理过了
nai.asyncChannel.sendMessage(
NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, redirectUrlBundle);
这边可以看到会将网络校验成功或者失败通过asyncChannel发送出去
NetworkAgent会处理
case CMD_REPORT_NETWORK_STATUS: {
String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
if (VDBG) {
log("CMD_REPORT_NETWORK_STATUS(" +
(msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
}
networkStatus(msg.arg1, redirectUrl);
break;
}
对应到WiFi就看下WiFi的具体实现
private class WifiNetworkAgent extends NetworkAgent {
WifiNetworkAgent(Looper l, Context c, String tag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(l, c, tag, ni, nc, lp, score, misc);
}
private int mLastNetworkStatus = -1; // To detect when the status really changes
@Override
protected void unwanted() {
// Ignore if we're not the current networkAgent.
if (this != mNetworkAgent) return;
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi unwanted score " + Integer.toString(mWifiInfo.score));
}
unwantedNetwork(NETWORK_STATUS_UNWANTED_DISCONNECT);
}
@Override
protected void networkStatus(int status, String redirectUrl) {
if (this != mNetworkAgent) return;
if (status == mLastNetworkStatus) return;
mLastNetworkStatus = status;
if (status == NetworkAgent.INVALID_NETWORK) {
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi networkStatus invalid, score="
+ Integer.toString(mWifiInfo.score));
}
unwantedNetwork(NETWORK_STATUS_UNWANTED_VALIDATION_FAILED);
} else if (status == NetworkAgent.VALID_NETWORK) {
if (mVerboseLoggingEnabled) {
log("WifiNetworkAgent -> Wifi networkStatus valid, score= "
+ Integer.toString(mWifiInfo.score));
}
mWifiMetrics.logStaEvent(StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK);
doNetworkStatus(status);
}
}
} else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
|| message.arg1 == NETWORK_STATUS_UNWANTED_VALIDATION_FAILED) {
Log.d(TAG, (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
? "NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN"
: "NETWORK_STATUS_UNWANTED_VALIDATION_FAILED"));
config = getCurrentWifiConfiguration();
if (config != null) {
// Disable autojoin
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN) {
mWifiConfigManager.setNetworkValidatedInternetAccess(
config.networkId, false);
mWifiConfigManager.updateNetworkSelectionStatus(config.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_NO_INTERNET_PERMANENT);
} else {
// stop collect last-mile stats since validation fail
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
mWifiDiagnostics.reportConnectionEvent(
WifiDiagnostics.CONNECTION_EVENT_FAILED);
mWifiConfigManager.incrementNetworkNoInternetAccessReports(
config.networkId);
// If this was not the last selected network, update network
// selection status to temporarily disable the network.
if (mWifiConfigManager.getLastSelectedNetwork() != config.networkId
&& !config.noInternetAccessExpected) {
Log.i(TAG, "Temporarily disabling network because of"
+ "no-internet access");
mWifiConfigManager.updateNetworkSelectionStatus(
config.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_NO_INTERNET_TEMPORARY);
}
}
}
}
若该网络不是用户最近选择的并且不认为是无网络的,则将其设置为临时无网络禁用,临时无网络禁用是不会触发断开的
WifiConnectivityManager
private class OnSavedNetworkUpdateListener implements
WifiConfigManager.OnSavedNetworkUpdateListener {
@Override
public void onSavedNetworkAdded(int networkId) {
updatePnoScan();
}
@Override
public void onSavedNetworkEnabled(int networkId) {
updatePnoScan();
}
@Override
public void onSavedNetworkRemoved(int networkId) {
updatePnoScan();
}
@Override
public void onSavedNetworkUpdated(int networkId) {
// User might have changed meteredOverride, so update capabilties
mStateMachine.updateCapabilities();
updatePnoScan();
}
@Override
public void onSavedNetworkTemporarilyDisabled(int networkId, int disableReason) {
if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return;
mConnectivityHelper.removeNetworkIfCurrent(networkId);
}
case CMD_NETWORK_STATUS:
if (message.arg1 == NetworkAgent.VALID_NETWORK) {
// stop collect last-mile stats since validation pass
removeMessages(CMD_DIAGS_CONNECT_TIMEOUT);
mWifiDiagnostics.reportConnectionEvent(
WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
mWifiScoreCard.noteValidationSuccess(mWifiInfo);
config = getCurrentWifiConfiguration();
if (config != null) {
// re-enable autojoin
mWifiConfigManager.updateNetworkSelectionStatus(
config.networkId,
WifiConfiguration.NetworkSelectionStatus
.NETWORK_SELECTION_ENABLE);
mWifiConfigManager.setNetworkValidatedInternetAccess(
config.networkId, true);
}
}
break;
和无网络临时禁用是相对的,会enable当前的ap config
ConnectivityService还有个8s的超时校验
private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
PROMPT_UNVALIDATED_DELAY_MS);
}
// How long to wait before putting up a "This network doesn't have an Internet connection,
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
private void handlePromptUnvalidated(Network network) {
if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null || !shouldPromptUnvalidated(nai)) {
return;
}
// Stop automatically reconnecting to this network in the future. Automatically connecting
// to a network that provides no or limited connectivity is not useful, because the user
// cannot use that network except through the notification shown by this method, and the
// notification is only shown if the network is explicitly selected by the user.
nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
// TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when
// NetworkMonitor detects the network is partial connectivity. Need to change the design to
// popup the notification immediately when the network is partial connectivity.
if (nai.partialConnectivity) {
showNetworkNotification(nai, NotificationType.PARTIAL_CONNECTIVITY);
} else {
showNetworkNotification(nai, NotificationType.NO_INTERNET);
}
}
看这一句
// Stop automatically reconnecting to this network in the future. Automatically connecting
// to a network that provides no or limited connectivity is not useful, because the user
// cannot use that network except through the notification shown by this method, and the
// notification is only shown if the network is explicitly selected by the user.
nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
NetworkAgent会处理
case CMD_PREVENT_AUTOMATIC_RECONNECT: {
preventAutomaticReconnect();
break;
}
对应到WiFi
@Override
protected void preventAutomaticReconnect() {
if (this != mNetworkAgent) return;
unwantedNetwork(NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN);
}
} else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
|| message.arg1 == NETWORK_STATUS_UNWANTED_VALIDATION_FAILED) {
Log.d(TAG, (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN
? "NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN"
: "NETWORK_STATUS_UNWANTED_VALIDATION_FAILED"));
config = getCurrentWifiConfiguration();
if (config != null) {
// Disable autojoin
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN) {
mWifiConfigManager.setNetworkValidatedInternetAccess(
config.networkId, false);
mWifiConfigManager.updateNetworkSelectionStatus(config.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_NO_INTERNET_PERMANENT);
}
设置为无网络并且永久禁用,同样无网络永久禁用并不会断开
@Override
public void onSavedNetworkPermanentlyDisabled(int networkId, int disableReason) {
// For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network
// because supplicant won't be trying to reconnect. If this is due to a
// preventAutomaticReconnect request from ConnectivityService, that service
// will disconnect as appropriate.
if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return;
mConnectivityHelper.removeNetworkIfCurrent(networkId);
updatePnoScan();
}
WiFi若在8s内校验不通过则会被永久禁用并将网络设置为无网络;
若校验未通过,则会被设置为临时禁用;
若校验通过,则会设置为enable并且网络可用
待研究一个问题:
若永久禁用后又被网络校验失败,会变成临时禁用么?从代码来看会的呀