美文网首页
Android之WIFI-网络可用性校验(NetworkMoni

Android之WIFI-网络可用性校验(NetworkMoni

作者: 锄禾豆 | 来源:发表于2022-01-30 09:39 被阅读0次

流程框架

WifiStateMachine(L2ConnectedState) 

NetworkAgent
|
通信:服务
|
ConnectivityService

NetworkAgentInfo

NeworkMonitor

注:
7.1

基础知识

StateMachine即状态机运用
AsyncChannel即双Handler通信机制运用

源码

frameworks/base/services/core/java/com/android/server/ConnectivityService.java
frameworks/base/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java

frameworks/base/core/java/com/android/net/NetworkAgent.java
frameworks/opt/net/wifi/services/core/java/com/android/server/wifi/WifiStateMachine.java

frameworks/base/core/java/com/android/internal/util/StateMachine.java
frameworks/base/core/java/com/android/internal/util/AsyncChannel.java

细节
1.WifiStateMachine在状态L2ConnectedState时,进行NetworkAgent初始化。
NetworkAgent初始化的过程建立与ConnectivityService通信

WifiStateMachine.L2ConnectedState
class L2ConnectedState extends State {
        @Override
        public void enter() {
            ······
            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
                    "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
                    mLinkProperties, 60, mNetworkMisc);
            ······
        }
}


WifiNetworkAgent(extends NetworkAgent)
    public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
            NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
        ······
        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
                Context.CONNECTIVITY_SERVICE);
        netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
                new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
        //cm.registerNetworkAgent把NetworkAgent和ConnectivityService建立连接
        //更多的细节方向,在于双handler跨进程通信,重点关注Messenger
    }

2.ConnectivityService的registerNetworkAgent创建NetworkAgentInfo

ConnectivityService.registerNetworkAgent
    public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
            int currentScore, NetworkMisc networkMisc) {
        ······
        final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
                linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
                mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);//NetworkAgent的messenger注入到NetworkAgentInfo,这样NetworkAgent与NetworkAgentInfo建立联系,注:双handler通信关注AsyncChannel
        ···
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));//走到handleRegisterNetworkAgent
        ······
    }

    private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
        if (VDBG) log("Got NetworkAgent Messenger");
        mNetworkAgentInfos.put(na.messenger, na);
        synchronized (mNetworkForNetId) {
            mNetworkForNetId.put(na.network.netId, na);
        }
        na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);//NetworkAgentInfo的asyncChannel把ConnectivityService中mTrackerHandler和NetworkAgent中messenger建立连接
        NetworkInfo networkInfo = na.networkInfo;
        na.networkInfo = null;
        updateNetworkInfo(na, networkInfo);
    }
    
    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
        ···
        if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
            ···
            networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
            ···
        }
        
    }

3.NetworkAgentInfo的初始化,创建NetworkMonitor,而NetworkMonitor则是监听网络的可用性

1)来源介绍
NetworkAgentInfo
    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
            LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
            NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
        ······
        mHandler = handler;//handler是ConnectivityService.mTrackerHandler
        networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
        ····
    }
    
NetworkMonitor // NetworkMonitor extends StateMachine即NetworkMonitor为状态机
    protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
            NetworkRequest defaultRequest, IpConnectivityLog logger) {
        ···
        mConnectivityServiceHandler = handler;//ConnectivityServiceHandler是ConnectivityService.mTrackerHandler
        ···
        addState(mDefaultState);
        addState(mValidatedState, mDefaultState);
        addState(mMaybeNotifyState, mDefaultState);
            addState(mEvaluatingState, mMaybeNotifyState);
            addState(mCaptivePortalState, mMaybeNotifyState);
        setInitialState(mDefaultState);
        ····
        start();
    }

2)ConnectivityService和NetworkMonitor通信介绍
a.ConnectivityService更新数据时,通过NetworkAgent通知NetworkMonitor。例如:
ConnectivityService.updateNetworkInfo
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);

b.NetworkMonitor收到数据时更新后,通过ConnectivityService.mTrackerHandler通知ConnectivityService。例如:
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                                NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));

3)ConnectivityService和WifiStateMachine通信介绍
a.AsyncChannel实现了跨服务通信
b.ConnectivityService.handleRegisterNetworkAgent建立连接
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);//这里把双方通信建立起来

c.ConnectivityService更新数据给WifiStateMachine,通过如下方式实现
nai.asyncChannel.sendMessage(
                                NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                                (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
                                0, redirectUrlBundle);
这是通知给WifiStateMachine的NetworkAgent

d.WifiStateMachine更新数据给ConnectivityService,通过如下方式实现
WifiStateMachine.setNetworkDetailedState
    mNetworkAgent.sendNetworkInfo(mNetworkInfo);
       NetworkAgent.queueOrSendMessage
例如
    private void queueOrSendMessage(Message msg) {
        synchronized (mPreConnectedQueue) {
            if (mAsyncChannel != null) {
                mAsyncChannel.sendMessage(msg);
            } else {
                mPreConnectedQueue.add(msg);
            }
        }
    }

4.NetworkMonitor

1)NetworkMonitor为状态机,默认状态为mDefaultState

2)当ConnectivityService的更新指令时,做状态切换
ConnectivityService.updateNetworkInfo
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
   
    private class DefaultState extends State {
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_NETWORK_CONNECTED:
                    logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
                    transitionTo(mEvaluatingState);//切换到mEvaluatingState状态
                    return HANDLED;
                ···
        }
        ···
    }
    
    private class EvaluatingState extends State {
        ···
        @Override
        public void enter() {
            ···
            sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
            ···
        }
        
        @Override
        public boolean processMessage(Message message) {
            switch (message.what) {
                case CMD_REEVALUATE:
                    ···
                    //关键方法,ping网络
                    //根据结果切换状态或更新数据
                    //关注isCaptivePortal
                    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));
                        ···
                        transitionTo(mCaptivePortalState);
                    } else {
                        ··· 
                        mConnectivityServiceHandler.sendMessage(obtainMessage(
                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
                                probeResult.redirectUrl));
                        
                    }
                    return HANDLED;
                ···
            }        
            ···
        }
        ···
    }
    
    protected CaptivePortalProbeResult isCaptivePortal() {//以http通信认证为主
        ···
        URL pacUrl = null, httpsUrl = null, httpUrl = null, fallbackUrl = null;
        ···
        final CaptivePortalProbeResult result;
        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);//请求http
        }
        ···
        return result;
    }
    
    private CaptivePortalProbeResult sendDnsAndHttpProbes(ProxyInfo proxy, URL url, int probeType) {
        ···
        return sendHttpProbe(url, probeType);
    }
    
    protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType) {//ping即通过HttpURLConnection验证
        HttpURLConnection urlConnection = null;
        int httpResponseCode = 599;
        String redirectUrl = null;
        final Stopwatch probeTimer = new Stopwatch().start();
        try {
            urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
            urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
            urlConnection.setUseCaches(false);
            final String userAgent = getCaptivePortalUserAgent(mContext);
            if (userAgent != null) {
               urlConnection.setRequestProperty("User-Agent", userAgent);
            }

            // Time how long it takes to get a response to our request
            long requestTimestamp = SystemClock.elapsedRealtime();

            httpResponseCode = urlConnection.getResponseCode();
            redirectUrl = urlConnection.getHeaderField("location");

            // Time how long it takes to get a response to our request
            long responseTimestamp = SystemClock.elapsedRealtime();

            validationLog(ValidationProbeEvent.getProbeName(probeType) + " " + url +
                    " time=" + (responseTimestamp - requestTimestamp) + "ms" +
                    " ret=" + httpResponseCode +
                    " 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.
            if (httpResponseCode == 200) {
                if (probeType == ValidationProbeEvent.PROBE_PAC) {
                    validationLog("PAC fetch 200 response interpreted as 204 response.");
                    httpResponseCode = 204;
                } else if (urlConnection.getContentLengthLong() == 0) {
                    // Consider 200 response with "Content-length=0" to not be a captive portal.
                    // There's no point in considering this a captive portal as the user cannot
                    // sign-in to an empty page. Probably the result of a broken transparent proxy.
                    // See http://b/9972012.
                    validationLog(
                        "200 response with Content-length=0 interpreted as 204 response.");
                    httpResponseCode = 204;
                } else if (urlConnection.getContentLengthLong() == -1) {
                    // When no Content-length (default value == -1), attempt to read a byte from the
                    // response. Do not use available() as it is unreliable. See http://b/33498325.
                    if (urlConnection.getInputStream().read() == -1) {
                        validationLog("Empty 200 response interpreted as 204 response.");
                        httpResponseCode = 204;
                    }
                }
            }
        } catch (IOException e) {
            validationLog("Probably not a portal: exception " + e);
            if (httpResponseCode == 599) {
                // TODO: Ping gateway and DNS server and log results.
            }
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
        logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
        return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
    }

adb控制

1)ping对应的网址自定义
adb shell settings put global captive_portal_use_https 0  //采用http, 关闭https
adb shell settings put global captive_portal_detection_enabled 1  //打开可用性
adb shell settings put global captive_portal_http_url http://connect.rom.miui.com/generate_204  //http访问网址

2)ConnectivityService
adb shell dumpsys connectivity //可查看当前网络的对应信息
例如:
C:\Users\99418>adb shell dumpsys connectivity
NetworkFactories for: WIFI_UT WIFI Ethernet

Active default network: 104

Current Networks:
  NetworkAgentInfo{ ni{[type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: "Galanz_Office", failover: false, available: true, roaming: false, metered: false]}  network{104}  nethandle{446693034718}  lp{{InterfaceName: wlan0 LinkAddresses: [fe80::ce4b:73ff:fed2:71a8/64,10.100.3.142/24,]  Routes: [fe80::/64 -> :: wlan0,10.100.3.0/24 -> 0.0.0.0 wlan0,0.0.0.0/0 -> 10.100.3.1 wlan0,] DnsAddresses: [172.1.1.20,114.114.114.114,] Domains: null MTU: 0 TcpBufferSizes: 524288,1048576,2097152,262144,524288,1048576}}  nc{[ Transports: WIFI Capabilities: NOT_METERED&INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN&VALIDATED&FOREGROUND LinkUpBandwidth>=1048576Kbps LinkDnBandwidth>=1048576Kbps SignalStrength: -66]}  Score{56}  everValidated{true}  lastValidated{true}  created{true} lingering{false} explicitlySelected{false} acceptUnvalidated{false} everCaptivePortalDetected{false} lastCaptivePortalDetected{false} }
    Requests: REQUEST:1 LISTEN:2 BACKGROUND_REQUEST:0 total:3
      NetworkRequest [ REQUEST id=1, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&NOT_VPN] ]
      NetworkRequest [ LISTEN id=3, [] ]
      NetworkRequest [ LISTEN id=4, [] ]
    Lingered:


3)WifiStateMachine
adb shell dumpsys wifi //可查WifiStateMachine具体信息

问题

1.ConnectivityManager.CONNECTIVITY_ACTION 跟wifi怎么关联起来?

ConnectivityService.updateNetworkInfo 接收来自WifiStateMachine的更新

而updateNetworkInfo包含了rematchNetworkAndRequests的调用

关键点在于mLegacyTypeTracker的add\update\remove中关联了ConnectivityManager.CONNECTIVITY_ACTION的处理

参考学习

https://blog.csdn.net/u010961631/article/details/48971823
https://blog.csdn.net/u010961631/article/details/48629601
https://www.cnblogs.com/ziyouchutuwenwu/p/11429284.html
https://blog.csdn.net/asahinokawa/article/details/80722178
https://www.cnblogs.com/qiqi715/p/9338699.html

相关文章

网友评论

      本文标题:Android之WIFI-网络可用性校验(NetworkMoni

      本文链接:https://www.haomeiwen.com/subject/xgvxkrtx.html