// Note: This call to isCaptivePortal() could take up to a minute. Resolving the
// server's IP addresses could hit the DNS timeout, and attempting connections
// to each of the server's several IP addresses (currently one IPv4 and one
// IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine
// will be unresponsive. isCaptivePortal() could be executed on another Thread
// if this is found to cause problems.
CaptivePortalProbeResult probeResult = isCaptivePortal();
if (probeResult.isSuccessful()) {  //这里返回204代表是一个有效的AP,之前认证过?
    // Transit EvaluatingPrivateDnsState to get to Validated
    // state (even if no Private DNS validation required).
} else if (probeResult.isPortal()) { //代表是一个Portal网络
     mLastPortalProbeResult = probeResult;
} else {
     final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
     sendMessageDelayed(msg, mReevaluateDelayMs);
     if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
         // Don't continue to blame UID forever.
     mReevaluateDelayMs *= 2;
     if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
         mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
  protected CaptivePortalProbeResult isCaptivePortal() {
        if (!mIsCaptivePortalCheckEnabled) {
            validationLog("Validation disabled.");
            return CaptivePortalProbeResult.SUCCESS;

        URL pacUrl = null;
        URL httpsUrl = mCaptivePortalHttpsUrl;
        URL httpUrl = mCaptivePortalHttpUrl;

        // On networks with a PAC instead of fetching a URL that should result in a 204
        // response, we instead simply fetch the PAC script. This is done for a few reasons:
        // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
        // until something like https://android-review.googlesource.com/#/c/115180/ lands.
        // Network.openConnection() will ignore network-specific PACs and instead fetch
        // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with
        // NO_PROXY is the fetch of the PAC itself.
        // 2. To proxy the generate_204 fetch through a PAC would require a number of things
        // happen before the fetch can commence, namely:
        // a) the PAC script be fetched
        // b) a PAC script resolver service be fired up and resolve the captive portal
        // server.
        // Network validation could be delayed until these prerequisities are satisifed or
        // could simply be left to race them. Neither is an optimal solution.
        // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
        // fact block fetching of the generate_204 URL which would lead to false negative
        // results for network validation.
        final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
        if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
            pacUrl = makeURL(proxyInfo.getPacFileUrl().toString());
            if (pacUrl == null) {
                return CaptivePortalProbeResult.FAILED;
        if ((pacUrl == null) && (httpUrl == null || httpsUrl == null)) {
            return CaptivePortalProbeResult.FAILED;
        long startTime = SystemClock.elapsedRealtime();

        CaptivePortalProbeResult result;
        if (pacUrl != null) {
            result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC);
        } else if (mUseHttps) {
            result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl);
        } else {
            result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP);
            // MIUI ADD: START
            if (!result.isSuccessful() && !result.isPortal()) {
                result = new CaptivePortalProbeResult(
                        NetworkMonitorInjector.sendHttpProbe(mNetworkAgentInfo, validationLogs));
            // END
        long endTime = SystemClock.elapsedRealtime();
        sendNetworkConditionsBroadcast(true /* response received */,
                result.isPortal() /* isCaptivePortal */,
                startTime, endTime);
        return result;
CaptivePortalProbeResult probeResult = isCaptivePortal



validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" +
                    " ret=" + httpResponseCode +
                    " request=" + requestHeader +
                    " headers=" + urlConnection.getHeaderFields());
// NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
// portal. The only example of this seen so far was a captive portal. For
// the time being go with prior behavior of assuming it's not a captive
// portal. If it is considered a captive portal, a different sign-in URL
// is needed (i.e. can't browse a 204). This could be the result of an HTTP
// proxy server.

这里的ret对应的是httpResponseCode = urlConnection.getResponseCode(); 可在日志中搜索PROBE_DNS以及PROBE_HTTP查看路由返回的信息

response code function
204 表示当前连接的网络是一个正常的AP,不用web验证
302 表示当前连接的网络是一个CaptivePortal的网络,需要登录web验证
599 则表示当前的验证失败了(也包括服务器端不回复,导致连接无法正常连接的情况)


12-20 13:54:43.751 D/NetworkMonitor/101( 1616): PROBE_HTTP http://connectivitycheck.gstatic.com/generate_204 time=23ms ret=302 request={Connection=[close], User-Agent=[Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.32 Safari/537.36]} headers={null=[HTTP/1.1 302 Moved Temporarily], Cache-Control=[no-cache], Connection=[close], Content-Length=[154], Content-Type=[text/html], Date=[Mon, 26 Aug 2019 15:01:53 GMT], Expires=[Thu, 01 Jan 1970 00:00:01 GMT], Location=[http://miwifi.com/diagnosis/index.html], Server=[nginx], X-Android-Received-Millis=[1576821283751], X-Android-Response-Source=[NETWORK 302], X-Android-Selected-Protocol=[http/1.1], X-Android-Sent-Millis=[1576821283749]}



  1. 连上一个portal网络,然后向小米服务器发送网络请求.
  2. 如果没有验证过,portal路由器不会让我们连接外网,并返回200及认证界面.如果已经验证过,直接放行,返回204(no content).
  3. 有没有认证过记录在portal路由器的认证服务器上,服务器记录多久,如何记录,以及服务器端的数据同步,手机均无法控制.






“为什么要努力?” “想去的地方很远,想要的东西很贵,喜欢的人很优秀,父母的白发,朋友的约定,周围人的嘲笑,以及,天生傲骨。”


*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>