1.流程梳理

1.1 初始化

1.1.1 WifiStateMachine

        // CHECKSTYLE:OFF IndentationCheck
        addState(mDefaultState);
            addState(mConnectModeState, mDefaultState);
                addState(mL2ConnectedState, mConnectModeState);
                    addState(mObtainingIpState, mL2ConnectedState);
                    addState(mConnectedState, mL2ConnectedState);
                    addState(mRoamingState, mL2ConnectedState);
                addState(mDisconnectingState, mConnectModeState);
                addState(mDisconnectedState, mConnectModeState);
        // CHECKSTYLE:ON IndentationCheck
    class L2ConnectedState extends State {
        class RssiEventHandler implements WifiNative.WifiRssiEventHandler {
            @Override
            public void onRssiThresholdBreached(byte curRssi) {
                if (mVerboseLoggingEnabled) {
                    Log.e(TAG, "onRssiThresholdBreach event. Cur Rssi = " + curRssi);
                }
                sendMessage(CMD_RSSI_THRESHOLD_BREACHED, curRssi);
            }
        }

        RssiEventHandler mRssiEventHandler = new RssiEventHandler();

        @Override
        public void enter() {
            mRssiPollToken++;
            if (mEnableRssiPolling) {
                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
            }
            if (mNetworkAgent != null) {
                loge("Have NetworkAgent when entering L2Connected");
                setNetworkDetailedState(DetailedState.DISCONNECTED);
            }
            setNetworkDetailedState(DetailedState.CONNECTING);

            final NetworkCapabilities nc;
            if (mWifiInfo != null && !mWifiInfo.getSSID().equals(WifiSsid.NONE)) {
                nc = new NetworkCapabilities(mNetworkCapabilitiesFilter);
                nc.setSSID(mWifiInfo.getSSID());
            } else {
                nc = mNetworkCapabilitiesFilter;
            }
            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
                    "WifiNetworkAgent", mNetworkInfo, nc, mLinkProperties, 60, mNetworkMisc);

            // We must clear the config BSSID, as the wifi chipset may decide to roam
            // from this point on and having the BSSID specified in the network block would
            // cause the roam to faile and the device to disconnect
            clearTargetBssid("L2ConnectedState");
            mCountryCode.setReadyForChange(false);
            mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
        }

从状态机变化来说应该是在 WiFi DisconnectedState 收到WifiMonitor.NETWORK_CONNECTION_EVENT消息即WiFi进入到competed状态继而进入到ObtainingIpState状态的时候,先进入其父状态ConnectModeState的enter方法时初始化的

看下构造函数及传参

    private class WifiNetworkAgent extends NetworkAgent {
        public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
                NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
            super(l, c, TAG, ni, nc, lp, score, misc);
        }

WifiNetworkAgent是WifiStateMachine的内部类,继承的NetworkAgent,构造方法是调用的父类的构造方法

 

1.1.2 NetworkAgent

/**
 * A Utility class for handling for communicating between bearer-specific
 * code and ConnectivityService.
 *
 * A bearer may have more than one NetworkAgent if it can simultaneously
 * support separate networks (IMS / Internet / MMS Apns on cellular, or
 * perhaps connections with different SSID or P2P for Wi-Fi).
 *
 * @hide
 */
public abstract class NetworkAgent extends Handler {

NetworkAgent如上面注释:

用于处理特定于承载的代码和ConnectivityService之间的通信的Utility类。

如果承载可以同时支持单独的网络(蜂窝上的IMS / Internet / MMS Apns,或者可能具有用于Wi-Fi的不同SSID或P2P的连接),则承载可以具有多于一个NetworkAgent。

个人理解为业务代码和ConnectivityService之间通信的桥梁,如名称所示,网络代理,本质上是个以AsyncChannel方式跨进程通信的handler。

    public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
            NetworkCapabilities nc, LinkProperties lp, int score) {
        this(looper, context, logTag, ni, nc, lp, score, null);
    }

    public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
            NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
        super(looper);
        LOG_TAG = logTag;
        mContext = context;
        if (ni == null || nc == null || lp == null) {
            throw new IllegalArgumentException();
        }

        if (VDBG) log("Registering NetworkAgent");
        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);
    }

从构造方法里可以看出大部分参数都传递给了ConnectivityService,看下ConnectivityManager的registerNetworkAgent

 

1.1.3 ConnectivityService

    /**
     * @hide
     * Register a NetworkAgent with ConnectivityService.
     * @return NetID corresponding to NetworkAgent.
     */
    public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
            NetworkCapabilities nc, int score, NetworkMisc misc) {
        try {
            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

看下ConnectivityService的实现

    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,从名称来看是储存网络代理信息的。

 

1.1.4 NetworkAgentInfo

/**
 * A bag class used by ConnectivityService for holding a collection of most recent
 * information published by a particular NetworkAgent as well as the
 * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests
 * interested in using it.  Default sort order is descending by score.
 */
// States of a network:
// --------------------
// 1. registered, uncreated, disconnected, unvalidated
//    This state is entered when a NetworkFactory registers a NetworkAgent in any state except
//    the CONNECTED state.
// 2. registered, uncreated, connecting, unvalidated
//    This state is entered when a registered NetworkAgent for a VPN network transitions to the
//    CONNECTING state (TODO: go through this state for every network, not just VPNs).
//    ConnectivityService will tell netd to create the network early in order to add extra UID
//    routing rules referencing the netID. These rules need to be in place before the network is
//    connected to avoid racing against client apps trying to connect to a half-setup network.
// 3. registered, uncreated, connected, unvalidated
//    This state is entered when a registered NetworkAgent transitions to the CONNECTED state.
//    ConnectivityService will tell netd to create the network if it was not already created, and
//    immediately transition to state #4.
// 4. registered, created, connected, unvalidated
//    If this network can satisfy the default NetworkRequest, then NetworkMonitor will
//    probe for Internet connectivity.
//    If this network cannot satisfy the default NetworkRequest, it will immediately be
//    transitioned to state #5.
//    A network may remain in this state if NetworkMonitor fails to find Internet connectivity,
//    for example:
//    a. a captive portal is present, or
//    b. a WiFi router whose Internet backhaul is down, or
//    c. a wireless connection stops transfering packets temporarily (e.g. device is in elevator
//       or tunnel) but does not disconnect from the AP/cell tower, or
//    d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes.
// 5. registered, created, connected, validated
//
// The device's default network connection:
// ----------------------------------------
// Networks in states #4 and #5 may be used as a device's default network connection if they
// satisfy the default NetworkRequest.
// A network, that satisfies the default NetworkRequest, in state #5 should always be chosen
// in favor of a network, that satisfies the default NetworkRequest, in state #4.
// When deciding between two networks, that both satisfy the default NetworkRequest, to select
// for the default network connection, the one with the higher score should be chosen.
//
// When a network disconnects:
// ---------------------------
// If a network's transport disappears, for example:
// a. WiFi turned off, or
// b. cellular data turned off, or
// c. airplane mode is turned on, or
// d. a wireless connection disconnects from AP/cell tower entirely (e.g. device is out of range
//    of AP for an extended period of time, or switches to another AP without roaming)
// then that network can transition from any state (#1-#5) to unregistered.  This happens by
// the transport disconnecting their NetworkAgent's AsyncChannel with ConnectivityManager.
// ConnectivityService also tells netd to destroy the network.
//
// When ConnectivityService disconnects a network:
// -----------------------------------------------
// If a network has no chance of satisfying any requests (even if it were to become validated
// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
//
// If the network was satisfying a foreground NetworkRequest (i.e. had been the highest scoring that
// satisfied the NetworkRequest's constraints), but is no longer the highest scoring network for any
// foreground NetworkRequest, then there will be a 30s pause to allow network communication to be
// wrapped up rather than abruptly terminated. During this pause the network is said to be
// "lingering". During this pause if the network begins satisfying a foreground NetworkRequest,
// ConnectivityService will cancel the future disconnection of the NetworkAgent's AsyncChannel, and
// the network is no longer considered "lingering". After the linger timer expires, if the network
// is satisfying one or more background NetworkRequests it is kept up in the background. If it is
// not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {

注释讲了一堆

  • 概要
  • 状态
  • 网络选择基于评分
  • 断开及保持

1.1.5 WSM与CS的桥梁

注册网络代理后续会发出一个EVENT_REGISTER_NETWORK_AGENT消息,这会通过asyncChannel建立起WifiStateMachine和ConnectivitySerivce通信的桥梁

mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));


                case EVENT_REGISTER_NETWORK_AGENT: {
                    handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
                    break;
                }



    private void handleRegisterNetworkAgent(NetworkAgentInfo nai) {
        if (VDBG) log("Got NetworkAgent Messenger");
        mNetworkAgentInfos.put(nai.messenger, nai);
        synchronized (mNetworkForNetId) {
            mNetworkForNetId.put(nai.network.netId, nai);
        }
        nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
        NetworkInfo networkInfo = nai.networkInfo;
        nai.networkInfo = null;
        updateNetworkInfo(nai, networkInfo);
        updateUids(nai, null, nai.networkCapabilities);
    }

1.1.6 WifiNetworkAgent的参数概要

            mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
                    "WifiNetworkAgent", mNetworkInfo, nc, mLinkProperties, 60, mNetworkMisc);

NetworkInfo

mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");

/**
 * Describes the status of a network interface.
 * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
 * the current network connection.
 */
public class NetworkInfo implements Parcelable {

NetworkCapabilities

/**
 * Representation of the capabilities of an active network. Instances are
 * typically obtained through
 * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)}
 * or {@link ConnectivityManager#getNetworkCapabilities(Network)}.
 * <p>
 * This replaces the old {@link ConnectivityManager#TYPE_MOBILE} method of
 * network selection. Rather than indicate a need for Wi-Fi because an
 * application needs high bandwidth and risk obsolescence when a new, fast
 * network appears (like LTE), the application should specify it needs high
 * bandwidth. Similarly if an application needs an unmetered network for a bulk
 * transfer it can specify that rather than assuming all cellular based
 * connections are metered and all Wi-Fi based connections are not.
 */
public final class NetworkCapabilities implements Parcelable {


    // Used to filter out requests we couldn't possibly satisfy.
    private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
        mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
        mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
        mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
        mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);

LinkProperties

    /**
     * The link properties of the wifi interface.
     * Do not modify this directly; use updateLinkProperties instead.
     */
    private LinkProperties mLinkProperties;

/**
 * Describes the properties of a network link.
 *
 * A link represents a connection to a network.
 * It may have multiple addresses and multiple gateways,
 * multiple dns servers but only one http proxy and one
 * network interface.
 *
 * Note that this is just a holder of data.  Modifying it
 * does not affect live networks.
 *
 */
public final class LinkProperties implements Parcelable {

链路属性看起来是在dhcp完成后更新并广播出来,并发给ConnectivityService

NetworkMisc

    // Provide packet filter capabilities to ConnectivityService.
    private final NetworkMisc mNetworkMisc = new NetworkMisc();

/**
 * A grab-bag of information (metadata, policies, properties, etc) about a
 * {@link Network}. Since this contains PII, it should not be sent outside the
 * system.
 *
 * @hide
 */
public class NetworkMisc implements Parcelable {

Score

    // This represents the last score received from the NetworkAgent.
    private int currentScore;

 

1.2 更新LinkProperties

    private void updateLinkProperties(LinkProperties newLp) {
        if (mVerboseLoggingEnabled) {
            log("Link configuration changed for netId: " + mLastNetworkId
                    + " old: " + mLinkProperties + " new: " + newLp);
        }
        // We own this instance of LinkProperties because IpClient passes us a copy.
        mLinkProperties = newLp;
        if (mNetworkAgent != null) {
            mNetworkAgent.sendLinkProperties(mLinkProperties);
        }

        if (getNetworkDetailedState() == DetailedState.CONNECTED) {
            // If anything has changed and we're already connected, send out a notification.
            // TODO: Update all callers to use NetworkCallbacks and delete this.
            sendLinkConfigurationChangedBroadcast();
        }

        if (mVerboseLoggingEnabled) {
            StringBuilder sb = new StringBuilder();
            sb.append("updateLinkProperties nid: " + mLastNetworkId);
            sb.append(" state: " + getNetworkDetailedState());

            if (mLinkProperties != null) {
                sb.append(" ");
                sb.append(getLinkPropertiesSummary(mLinkProperties));
            }
            logd(sb.toString());
        }
    }

NetworkAgent

    /**
     * Sent by the NetworkAgent to ConnectivityService to pass the current
     * NetworkProperties.
     * obj = NetworkProperties
     */
    public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;

    /**
     * Called by the bearer code when it has new LinkProperties data.
     */
    public void sendLinkProperties(LinkProperties linkProperties) {
        queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
    }

ConnectivityService

                case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
                    handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
                    break;
                }

    public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
        if (getNetworkAgentInfoForNetId(nai.network.netId) != nai) {
            // Ignore updates for disconnected networks
            return;
        }
        // newLp is already a defensive copy.
        newLp.ensureDirectlyConnectedRoutes();
        if (VDBG) {
            log("Update of LinkProperties for " + nai.name() +
                    "; created=" + nai.created +
                    "; everConnected=" + nai.everConnected);
        }
        LinkProperties oldLp = nai.linkProperties;
        synchronized (nai) {
            nai.linkProperties = newLp;
        }
        if (nai.everConnected) {
            updateLinkProperties(nai, oldLp);
        }
    }

 

1.3 更新NetworkInfo

mNetworkAgent.sendNetworkInfo(mNetworkInfo);

NetworAgent

    /**
     * Called by the bearer code when it has new NetworkInfo data.
     */
    public void sendNetworkInfo(NetworkInfo networkInfo) {
        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
    }


    /**
     * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
     * ConnectivityService to pass the current NetworkInfo (connection state).
     * Sent when the NetworkInfo changes, mainly due to change of state.
     * obj = NetworkInfo
     */
    public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;

ConnectivityService

                case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
                    NetworkInfo info = (NetworkInfo) msg.obj;
                    updateNetworkInfo(nai, info);
                    break;
                }

这边接了(一百五十七)Android P 梳理网络校验后续结果上报 的updateNetworkInfo的流程

 

1.4 更新NetworkCapabilities

    private void updateCapabilities(WifiConfiguration config) {
        if (mNetworkAgent == null) {
            return;
        }

        final NetworkCapabilities result = new NetworkCapabilities(mDfltNetworkCapabilities);

        if (mWifiInfo != null && !mWifiInfo.isEphemeral()) {
            result.addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
        } else {
            result.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
        }

        if (mWifiInfo != null && !WifiConfiguration.isMetered(config, mWifiInfo)) {
            result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
        } else {
            result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
        }

        if (mWifiInfo != null && mWifiInfo.getRssi() != WifiInfo.INVALID_RSSI) {
            result.setSignalStrength(mWifiInfo.getRssi());
        } else {
            result.setSignalStrength(NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED);
        }

        if (mWifiInfo != null && !mWifiInfo.getSSID().equals(WifiSsid.NONE)) {
            result.setSSID(mWifiInfo.getSSID());
        } else {
            result.setSSID(null);
        }

        mNetworkAgent.sendNetworkCapabilities(result);
    }

后续略

 

2.总结

NetworkAgent为网络相关业务代码和ConnectivityService之间通信的桥梁,如名称所示,网络代理,本质上是个以AsyncChannel方式实现跨进程通信的handler。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐