Android9.0 网络评分之--NetworkMonitor

Posted Mrsongs的心情杂货铺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android9.0 网络评分之--NetworkMonitor相关的知识,希望对你有一定的参考价值。

一、NetworkMonitor的初始化流程

ConnectivityService 中registerNetworkAgent 会创建一个NetworkAgentInfo ,而在NetworkAgentInfo 的构造函数中会创建NetworkMonitor
public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc)
enforceConnectivityInternalPermission();

    LinkProperties lp = new LinkProperties(linkProperties);
    lp.ensureDirectlyConnectedRoutes();
    // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
    // satisfies mDefaultRequest.
    final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
    final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
            new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
            mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
    // Make sure the network capabilities reflect what the agent info says.
    nai.networkCapabilities = mixInCapabilities(nai, nc);
    synchronized (this) 
        nai.networkMonitor.systemReady = mSystemReady;
    
    final String extraInfo = networkInfo.getExtraInfo();
    final String name = TextUtils.isEmpty(extraInfo)
            ? nai.networkCapabilities.getSSID() : extraInfo;
    addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name);
    if (DBG) log("registerNetworkAgent " + nai);
    mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
    return nai.network.netId;

NetworkAgentInfo的构造函数中会调用mConnService.createNetworkMonitor创建Monitor
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)
this.messenger = messenger;
asyncChannel = ac;
network = net;
networkInfo = info;
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
mConnService = connService;
mContext = context;
mHandler = handler;
networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;

ConnectivityService中createNetworkMonitor
@VisibleForTesting
public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo nai, NetworkRequest defaultRequest)
return new NetworkMonitor(context, handler, nai, defaultRequest);

NetwrokMonitor的构造函数中会添加各个状态
@VisibleForTesting
protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
NetworkRequest defaultRequest, IpConnectivityLog logger,
NetworkMonitorSettings settings)
// Add suffix indicating which NetworkMonitor we’re talking about.
super(TAG + networkAgentInfo.name());

    // Logs with a tag of the form given just above, e.g.
    //     <timestamp>   862  2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
    setDbg(VDBG);

    mContext = context;
    mMetricsLog = logger;
    mConnectivityServiceHandler = handler;
    mSettings = settings;
    mNetworkAgentInfo = networkAgentInfo;
    mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network());
    mNetId = mNetwork.netId;
    mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    mDefaultRequest = defaultRequest;

    addState(mDefaultState);
    addState(mMaybeNotifyState, mDefaultState);
        addState(mEvaluatingState, mMaybeNotifyState);
        addState(mCaptivePortalState, mMaybeNotifyState);
    addState(mEvaluatingPrivateDnsState, mDefaultState);
    addState(mValidatedState, mDefaultState);
    setInitialState(mDefaultState);

    mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
    mUseHttps = getUseHttpsValidation();
    mCaptivePortalUserAgent = getCaptivePortalUserAgent();
    mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
    mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context));
    mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
    mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();

    start();

NetworkMonitor 的各个状态
NetworkMonitor共有七个状态机,分别是:DefaultState、OfflineState、ValidatedState、EvaluatingState、UserPromptedState、CaptivePortalState、LingeringState。他们的作用是:
DefaultState
----这是默认的状态机,也是其他所有状态机的父状态,主要处理网络连接的主要状态变化,包括连接、断开、测试、延时等模式。
EvaluatingState
----验证状态,网络连上时,将会进入该状态,然后会Ping网络,判断当前网络有效性,并决定下一步是进入ValidatedState还是OfflineState或者UserPromptedState。
OfflineState
----脱机状态,当Ping网络时,始终没有任何回应时,就会进入该状态。
ValidatedState
----验证通过的状态,当Ping通网络时,说明当前的网络是通畅的,将会进入该状态。
UserPromptedState
----验证失败状态,当Ping网络时,网络给出了重定向异常,比如接入中国移动时会跳入移动的帐号认证页面,需要用户进行网络登录后才可以继续上网。此时一般需要在界面上提示用户。
CaptivePortalState
----当网络被测试失败时进入UserPromptedState后,用户可以通过发送ACTION_SIGN_IN_REQUESTED的消息来进入CaptivePortalState状态,该状态中将会监听ACTION_CAPTIVE_PORTAL_LOGGED_IN消息,并可直接由该消息指定进入ValidatedState或者OfflineState模式。

二、NetworkMonitor的工作流程:

首先在ConnectivityService中的UpdateNetworkInfo中,调用networkMonitor 发送CMD_NETWORK_CONNECTED
if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED)
networkAgent.everConnected = true;

        handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
        updateLinkProperties(networkAgent, null);
        notifyIfacesChangedForNetworkStats();

        networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
        scheduleUnvalidatedPrompt(networkAgent);

然后我们看到NetworkMonitor中由DefaultState 进入了 EvaluatingState 状态
private class DefaultState extends State
@Override
public boolean processMessage(Message message)
switch (message.what)
case CMD_NETWORK_CONNECTED:
logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
transitionTo(mEvaluatingState);
return HANDLED;

在进入EvaluatingState 状态的时候会发送 CMD_REEVALUATE消息在内部处理我们,这个地方是ping网络的,原生的是ping WWW.Google.com ,这里定制ping的是8.8.8.8 。
private class EvaluatingState extends State
private int mReevaluateDelayMs;
private int mAttempts;

    @Override
    public void enter() 
        // If we have already started to track time spent in EvaluatingState
        // don't reset the timer due simply to, say, commands or events that
        // cause us to exit and re-enter EvaluatingState.
        if (!mEvaluationTimer.isStarted()) 
            mEvaluationTimer.start();
        
        sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
        if (mUidResponsibleForReeval != INVALID_UID) 
            TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
            mUidResponsibleForReeval = INVALID_UID;
        
        mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
        mAttempts = 0;
    

    @Override
    public boolean processMessage(Message message) 
        switch (message.what) 
            case CMD_REEVALUATE:
                if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
                    return HANDLED;
                // Don't bother validating networks that don't satisfy the default request.
                // This includes:
                //  - VPNs which can be considered explicitly desired by the user and the
                //    user's desire trumps whether the network validates.
                //  - Networks that don't provide Internet access.  It's unclear how to
                //    validate such networks.
                //  - Untrusted networks.  It's unsafe to prompt the user to sign-in to
                //    such networks and the user didn't express interest in connecting to
                //    such networks (an app did) so the user may be unhappily surprised when
                //    asked to sign-in to a network they didn't want to connect to in the
                //    first place.  Validation could be done to adjust the network scores
                //    however these networks are app-requested and may not be intended for
                //    general usage, in which case general validation may not be an accurate
                //    measure of the network's quality.  Only the app knows how to evaluate
                //    the network so don't bother validating here.  Furthermore sending HTTP
                //    packets over the network may be undesirable, for example an extremely
                //    expensive metered network, or unwanted leaking of the User Agent string.
                if (!isValidationRequired()) 
                    validationLog("Network would not satisfy default request, not validating");
                    transitionTo(mValidatedState);
                    return HANDLED;
                
               CaptivePortalProbeResult probeResult = isCaptivePortal();
                if (probeResult.isSuccessful()) 
                    // Transit EvaluatingPrivateDnsState to get to Validated
                    // state (even if no Private DNS validation required).
                    transitionTo(mEvaluatingPrivateDnsState);

这里面会根据isCaptivePortal()的结果,最终会进入EvaluatingPrivateDnsState状态。
进入的时候会发送CMD_EVALUATE_PRIVATE_DNS消息,
private class EvaluatingPrivateDnsState extends State
private int mPrivateDnsReevalDelayMs;
private PrivateDnsConfig mPrivateDnsConfig;

    @Override
    public void enter() 
        mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS;
        mPrivateDnsConfig = null;
        sendMessage(CMD_EVALUATE_PRIVATE_DNS);
    

    @Override
    public boolean processMessage(Message msg) 
        switch (msg.what) 
            case CMD_EVALUATE_PRIVATE_DNS:
                if (inStrictMode()) 
                    //if (!isStrictModeHostnameResolved()) 
                        //resolveStrictModeHostname();
                        Log.d(TAG, "Evaluating [usb0] network availability.");
                        if (isStrictModeHostnameResolved()) 
                        Log.d(TAG, "Evaluating [usb0] network availability - [OK]");
                            //notifyPrivateDnsConfigResolved();
                            handlePrivateDnsEvaluationSuccess();
                         else 
                            Log.d(TAG, "Evaluating [usb0] network availability - [NOK]");
                            handlePrivateDnsEvaluationFailure();
                            break;
                        
                    //

                    // Look up a one-time hostname, to bypass caching.
                    //
                    // Note that this will race with ConnectivityService
                    // code programming the DNS-over-TLS server IP addresses
                    // into netd (if invoked, above). If netd doesn't know
                    // the IP addresses yet, or if the connections to the IP
                    // addresses haven't yet been validated, netd will block
                    // for up to a few seconds before failing the lookup.
                    //if (!sendPrivateDnsProbe()) 
                        //handlePrivateDnsEvaluationFailure();
                        //break;
                    //
                

                // All good!
                transitionTo(mValidatedState);
                break;
            default:
                return NOT_HANDLED;
        
        return HANDLED;
    

isStrictModeHostnameResolved 主要是ping 8.8.8.8和百度,原生的是ping Google ,这里被修改了
private boolean isStrictModeHostnameResolved()
// here check the sysprop for tbox internet avaliablity
// this prop is set-unset by tbox service by which will manage tbox connectivity
boolean status = false;
for (int i = 0; i < DNS_PROBE_TIMES; i ++)
try
status = InetAddress.getByName(GOOGLE_DNS).isReachable(DNS_PROBE_TIMEOUT);
catch (IOException e)
status = false;

if (status)
Log.d(TAG, “isStrictModeHostnameResolved: Host [” + GOOGLE_DNS + “] is REACHABLE.”);
break;
else
Log.d(TAG, “isStrictModeHostnameResolved: Host [” + GOOGLE_DNS + “] is UN-REACHABLE.”);
try
status = InetAddress.getByName(BAIDU_DNS).isReachable(DNS_PROBE_TIMEOUT);
catch (IOException e)
status = false;

if (status)
Log.d(TAG, “isStrictModeHostnameResolved: Host [” + BAIDU_DNS + “] is REACHABLE.”);
break;
else
Log.d(TAG, “isStrictModeHostnameResolved: Host [” + BAIDU_DNS + “] is UN-REACHABLE.”);


        return status;
    

成功后调用handlePrivateDnsEvaluationSuccess进入循环,然后接着进入。ValidatedState状态之后发送EVENT_NETWORK_TESTED 通知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, mNetId, null));
mValidations++;

    @Override
    public boolean processMessage(Message message) 
        switch (message.what) 
            case CMD_NETWORK_CONNECTED:
                transitionTo(mValidatedState);
                break;
            case CMD_EVALUATE_PRIVATE_DNS:
                transitionTo(mEvaluatingPrivateDnsState);
                break;
            default:
                return NOT_HANDLED;
        
        return HANDLED;
    

ConnectivityService根据 EVENT_NETWORK_TESTED更新网络状态:
private boolean maybeHandleNetworkMonitorMessage(Message msg)
switch (msg.what)
default:
return false;
case NetworkMonitor.EVENT_NETWORK_TESTED:
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;

                final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                final boolean wasValidated = nai.lastValidated;
                final boolean wasDefault = isDefaultNetwork(nai);

                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);
                
                updateInetCondition(nai);
                // Let the NetworkAgent know the state of its network
                Bundle redirectUrlBundle = new Bundle();
                redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
                nai.asyncChannel.sendMessage(
                        NetworkAgent.CMD_REPORT_NETWORK_STATUS,
                        (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
                        0, redirectUrlBundle);
                if (wasValidated && !nai.lastValidated) 
					nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
                    handleNetworkUnvalidated(nai);
                 else if(!wasValidated && nai.lastValidated) 
					nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
					handleNetworkValidated(nai);
                 else if(!wasValidated && !nai.lastValidated) 
                    if(!nai.everFirstUnvalidated) 
                        log(nai.name() + " ever FRIST Unvalidated and notify SUSPENDED.");
                        nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
                        handleNetworkUnvalidated(nai);
                        nai.everFirstUnvalidated = true;
                    
                
                break;
            

以上是关于Android9.0 网络评分之--NetworkMonitor的主要内容,如果未能解决你的问题,请参考以下文章

Android9.0 网络框架之--Tethering 热点

Android9.0 网络框架之--Tethering 热点

Android之网络请求提示Cleartext HTTP traffic to dev*******.com not permitted

Android之网络请求提示Cleartext HTTP traffic to dev*******.com not permitted

Android 9.0网络权限适配

Android9.0无法加载图片及访问不了服务器问题