public class NetworkMonitor extends StateMachine {
private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204";
private static final String DEFAULT_HTTP_URL =
"http://connectivitycheck.gstatic.com/generate_204";
private static final String DEFAULT_FALLBACK_URL = "http://www.google.com/gen_204";
private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/52.0.2743.82 Safari/537.36";
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
// Before network has been evaluated this many times, ignore repeated reevaluate requests.
private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
private int mReevaluateToken = 0;
private static String getCaptivePortalServerHttpsUrl(Context context) {
return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
}
public static String getCaptivePortalServerHttpUrl(Context context) {
return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
}
private static String getCaptivePortalFallbackUrl(Context context) {
return getSetting(context,
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
}
private static String getCaptivePortalUserAgent(Context context) {
return getSetting(context, Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
}
private class DefaultState extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_NETWORK_CONNECTED:
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_FORCE_REEVALUATION:
case CMD_CAPTIVE_PORTAL_RECHECK:
transitionTo(mEvaluatingState);
return HANDLED;
}
}
// 网络评分状态
private class EvaluatingState extends State {
// 延时尝试时间
private int mReevaluateDelayMs;
// 尝试次数
private int mAttempts;
@Override
public void enter() {
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_REEVALUATE:
CaptivePortalProbeResult probeResult = isCaptivePortal();
if (probeResult.isSuccessful()) {
transitionTo(mValidatedState);
} else if (probeResult.isPortal()) {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
} else {
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
mConnectivityServiceHandler.sendMessage(obtainMessage(
EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
probeResult.redirectUrl));
if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
// Don't continue to blame UID forever.
TrafficStats.clearThreadStatsUid();
}
mReevaluateDelayMs *= 2;
if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
}
}
return HANDLED;
}
}
// 检查结果通知ConnectivityService
private class ValidatedState extends State {
@Override
public void enter() {
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
mValidations++;
}
/**
* 网络连通检查结果
*/
@VisibleForTesting
public static final class CaptivePortalProbeResult {
static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(599);
private final int mHttpResponseCode; // HTTP response code returned from Internet probe.
final String redirectUrl; // Redirect destination returned from Internet probe.
final String detectUrl; // URL where a 204 response code indicates
// captive portal has been appeased.
public CaptivePortalProbeResult(
int httpResponseCode, String redirectUrl, String detectUrl) {
mHttpResponseCode = httpResponseCode;
this.redirectUrl = redirectUrl;
this.detectUrl = detectUrl;
}
public CaptivePortalProbeResult(int httpResponseCode) {
this(httpResponseCode, null, null);
}
boolean isSuccessful() { return mHttpResponseCode == 204; }
boolean isPortal() {
return !isSuccessful() && mHttpResponseCode >= 200 && mHttpResponseCode <= 399;
}
}
@VisibleForTesting
protected CaptivePortalProbeResult isCaptivePortal() {
-------------------------------------------------------------
if (pacUrl == null) {
httpsUrl = makeURL(getCaptivePortalServerHttpsUrl(mContext));
httpUrl = makeURL(getCaptivePortalServerHttpUrl(mContext));
fallbackUrl = makeURL(getCaptivePortalFallbackUrl(mContext));
if (httpUrl == null || httpsUrl == null) {
return CaptivePortalProbeResult.FAILED;
}
}
-----------------------------------------------------------------------
if (pacUrl != null) {
result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC);
} else if (mUseHttps) {
result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl, fallbackUrl);
} else {
result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP);
}
----------------------------------------------------------------------
sendNetworkConditionsBroadcast(true /* response received */,
result.isPortal() /* isCaptivePortal */,
startTime, endTime);
}
private CaptivePortalProbeResult sendDnsAndHttpProbes(ProxyInfo proxy, URL url, int probeType) {
// Pre-resolve the captive portal server host so we can log it.
// Only do this if HttpURLConnection is about to, to avoid any potentially
// unnecessary resolution.
final String host = (proxy != null) ? proxy.getHost() : url.getHost();
sendDnsProbe(host);
return sendHttpProbe(url, probeType);
}
}