【定制Android系统】Android 7.1 实现连接 Captive Portal WiFi 时自动弹出登录页面

需求:系统连接WiFi时,若连接到需要登录的WiFi,则弹出登录页面。
说明:像 MIUI 等系统,现在的表现是弹出一个专门的页面(虽然我也不知道这个页面从哪儿能再次打开,以至于每次想更换账号都很费劲)。但是本文实现的是弹出一个普通的浏览器页面。
解决方案:在 NetworkMonitor.java 的 processMessage 中,当 Android 原生系统判断到需要登录时,发送 intent 打开任意网页,以使之自动重定向到登录页面。

NetworkMonitor.java 中这部分源码为:

508        @Override
509        public boolean processMessage(Message message) {
510            switch (message.what) {
511                case CMD_REEVALUATE:
512                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
513                        return HANDLED;
514                    // Don't bother validating networks that don't satisify the default request.
515                    // This includes:
516                    //  - VPNs which can be considered explicitly desired by the user and the
517                    //    user's desire trumps whether the network validates.
518                    //  - Networks that don't provide internet access.  It's unclear how to
519                    //    validate such networks.
520                    //  - Untrusted networks.  It's unsafe to prompt the user to sign-in to
521                    //    such networks and the user didn't express interest in connecting to
522                    //    such networks (an app did) so the user may be unhappily surprised when
523                    //    asked to sign-in to a network they didn't want to connect to in the
524                    //    first place.  Validation could be done to adjust the network scores
525                    //    however these networks are app-requested and may not be intended for
526                    //    general usage, in which case general validation may not be an accurate
527                    //    measure of the network's quality.  Only the app knows how to evaluate
528                    //    the network so don't bother validating here.  Furthermore sending HTTP
529                    //    packets over the network may be undesirable, for example an extremely
530                    //    expensive metered network, or unwanted leaking of the User Agent string.
531                    if (!mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
532                            mNetworkAgentInfo.networkCapabilities)) {
533                        validationLog("Network would not satisfy default request, not validating");
534                        transitionTo(mValidatedState);
535                        return HANDLED;
536                    }
537                    mAttempts++;
538                    // Note: This call to isCaptivePortal() could take up to a minute. Resolving the
539                    // server's IP addresses could hit the DNS timeout, and attempting connections
540                    // to each of the server's several IP addresses (currently one IPv4 and one
541                    // IPv6) could each take SOCKET_TIMEOUT_MS.  During this time this StateMachine
542                    // will be unresponsive. isCaptivePortal() could be executed on another Thread
543                    // if this is found to cause problems.
544                    CaptivePortalProbeResult probeResult = isCaptivePortal();
545                    if (probeResult.isSuccessful()) {
546                        transitionTo(mValidatedState);
547                    } else if (probeResult.isPortal()) {
548                        mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
549                                NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));
550                        mLastPortalProbeResult = probeResult;
551                        transitionTo(mCaptivePortalState);
552                    } else {
553                        final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
554                        sendMessageDelayed(msg, mReevaluateDelayMs);
555                        logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
556                        mConnectivityServiceHandler.sendMessage(obtainMessage(
557                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
558                                probeResult.redirectUrl));
559                        if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
560                            // Don't continue to blame UID forever.
561                            TrafficStats.clearThreadStatsUid();
562                        }
563                        mReevaluateDelayMs *= 2;
564                        if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
565                            mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
566                        }
567                    }
568                    return HANDLED;
569                case CMD_FORCE_REEVALUATION:
570                    // Before IGNORE_REEVALUATE_ATTEMPTS attempts are made,
571                    // ignore any re-evaluation requests. After, restart the
572                    // evaluation process via EvaluatingState#enter.
573                    return (mAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED;
574                default:
575                    return NOT_HANDLED;
576            }
577        }

原理可见下方链接。大概意思是,当WiFi成功连接后,NetworkMonitor 会用一个 CaptivePortalProbeResult=isCaptivePortal() 来判断当前的网络是否是 需要登录验证的。并且给出了各种结果,第一种(probeResult.isSuccessful())是连接了需验证的WiFi,但已成功验证。第二种(probeResult.isPortal())正是我们需要的,需要验证的。因此,这段代码可以修改为如下内容:

           CaptivePortalProbeResult probeResult = isCaptivePortal();
           if (probeResult.isSuccessful()) {
                transitionTo(mValidatedState);
           } else if (probeResult.isPortal()) {
                // Add by Enoch : Pop up window to log in portal wifi
                Log.d(TAG, "ENOCH:::the wifi is CaptivePortal. start a browser with google.com");
                Intent logIntent = new Intent();
                logIntent.setAction("android.intent.action.VIEW");
                logIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                Uri urlDefault = Uri.parse("http://www.google.com");
                logIntent.setData(urlDefault);
                mContext.startActivityAsUser(logIntent, UserHandle.CURRENT_OR_SELF);
                // Add by Enoch
                mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));
                mLastPortalProbeResult = probeResult;
                transitionTo(mCaptivePortalState);
           } else {

关于 Android 网络连接评分机制 NetworkMonitor 的介绍,见: https://blog.csdn.net/u010961631/article/details/48971823
如果是通过应用实现此功能,而非在Android系统源码中进行修改,则见:https://blog.csdn.net/u012382509/article/details/53219991

你可能感兴趣的:(Android)