Wifi模块—源码分析获取IP地址(Android P)

一 前言

        上一篇分析了连接AP的过程,当底层完成wifi连接,便会事件通知WifiMonitor,WifiMonitor监听到该事件后发送消息NETWORK_CONNECTION_EVENT。

                                       

二 代码具体流程

 1 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMonitor.java

 底层完成wifi连接,便会事件通知WifiMonitor,WifiMonitor监听到该事件后发送消息NETWORK_CONNECTION_EVENT。

/**
* Broadcast the network connection event to all the handlers registered for this event.
*
* @param iface Name of iface on which this occurred.
* @param networkId ID of the network in wpa_supplicant.
* @param bssid BSSID of the access point.
*/
public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
    sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
}

2 frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

WifiStateMachine此时处于DisconnectedState,该状态未能处理该消息,抛给它的父状态ConnectModeState处理。

case WifiMonitor.NETWORK_CONNECTION_EVENT:
    if (mVerboseLoggingEnabled) log("Network connection established");
    mLastNetworkId = message.arg1;
    mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
    mLastBssid = (String) message.obj;
    reasonCode = message.arg2;
    // TODO: This check should not be needed after WifiStateMachinePrime refactor.
    // Currently, the last connected network configuration is left in
    // wpa_supplicant, this may result in wpa_supplicant initiating connection
    // to it after a config store reload. Hence the old network Id lookups may not
    // work, so disconnect the network and let network selector reselect a new
    // network.
    config = getCurrentWifiConfiguration();
    if (config != null) {
        mWifiInfo.setBSSID(mLastBssid);
        mWifiInfo.setNetworkId(mLastNetworkId);
        mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));

        ScanDetailCache scanDetailCache =
                mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
        if (scanDetailCache != null && mLastBssid != null) {
            ScanResult scanResult = scanDetailCache.getScanResult(mLastBssid);
            if (scanResult != null) {
                mWifiInfo.setFrequency(scanResult.frequency);
            }
        }
        mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
        // We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
        if (config.enterpriseConfig != null
                && TelephonyUtil.isSimEapMethod(
                        config.enterpriseConfig.getEapMethod())) {
            String anonymousIdentity =
                    mWifiNative.getEapAnonymousIdentity(mInterfaceName);
            if (anonymousIdentity != null) {
                config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
            } else {
                Log.d(TAG, "Failed to get updated anonymous identity"
                        + " from supplicant, reset it in WifiConfiguration.");
                config.enterpriseConfig.setAnonymousIdentity(null);
            }
            mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
        }
        sendNetworkStateChangeBroadcast(mLastBssid);
        transitionTo(mObtainingIpState);
    } else {
        logw("Connected to unknown networkId " + mLastNetworkId
                + ", disconnecting...");
        sendMessage(CMD_DISCONNECT);
    }
    break;

可以看到一些操作,将ap的相关属性值赋给mWifiInfo。

                         mWifiInfo.setBSSID(mLastBssid)
                         mWifiInfo.setNetworkId(mLastNetworkId)
                         mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName))

更新Wifi的状态信息NetworkInfo,mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID)

并调用sendNetworkStateChangeBroadcast(mLastBssid)。

private void sendNetworkStateChangeBroadcast(String bssid) {
    Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
    networkInfo.setExtraInfo(null);
    intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
    //TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

调用mContext.sendStickyBroadcastAsUser发送广播WifiManager.NETWORK_STATE_CHANGED_ACTION。

然后转到获取ip的状态ObtainingIpState,transitionTo(mObtainingIpState),首先会进入它的父状态L2ConnectedState。

@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);
}

将mNetworkInfo状态设为CONNECTING,即调用setNetworkDetailedState(DetailedState.CONNECTING);创建了一个WifiNetworkAgent对象,关于WifiNetworkAgent的注册过程这次略过。

case WifiMonitor.NETWORK_CONNECTION_EVENT:
    mWifiInfo.setBSSID((String) message.obj);
    mLastNetworkId = message.arg1;
    mWifiInfo.setNetworkId(mLastNetworkId);
    mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName));
    if(!mLastBssid.equals(message.obj)) {
        mLastBssid = (String) message.obj;
        sendNetworkStateChangeBroadcast(mLastBssid);
    }
    break;

看sendNetworkStateChangeBroadcast(mLastBssid)。

private void sendNetworkStateChangeBroadcast(String bssid) {
    Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
    networkInfo.setExtraInfo(null);
    intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
    //TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

          调用mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL)发送广播WifiManager.NETWORK_STATE_CHANGED_ACTION。

4 frameworks/base/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java

接收到广播WifiManager.NETWORK_STATE_CHANGED_ACTION。

/**
*  Receiver for handling broadcasts.
*
*  This receiver is registered on the WorkHandler.
*/
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            updateWifiState(
                    intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                            WifiManager.WIFI_STATE_UNKNOWN));
        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
            mStaleScanResults = false;

            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            // TODO(sghuman): Refactor these methods so they cannot result in duplicate
            // onAccessPointsChanged updates being called from this intent.
            NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            updateNetworkInfo(info);
            fetchScansAndConfigsAndUpdateAccessPoints();
        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
            NetworkInfo info =
                    mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
            updateNetworkInfo(info);
        }
    }
};

WifiTracker接收到广播开始刷新fetchScansAndConfigsAndUpdateAccessPoints()。

5 packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            handleWifiStateChanged(mWifiManager.getWifiState());
        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
            if (!mConnected.get()) {
                handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
                        intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            mConnected.set(info.isConnected());
            handleStateChanged(info.getDetailedState());
        }
    }
};

应用层Settings模块接收到广播进行状态的相关处理handleWifiStateChanged(mWifiManager.getWifiState())。

接着WifiStateMachine进入ObtainingIpState状态,在该状态下完成ip配置,分为静态ip和动态ip。


    @Override
    public void enter() {
        final WifiConfiguration currentConfig = getCurrentWifiConfiguration();
        final boolean isUsingStaticIp =
                (currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
        if (mVerboseLoggingEnabled) {
            final String key = currentConfig.configKey();
            log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
                    + " " + key + " "
                    + " roam=" + mIsAutoRoaming
                    + " static=" + isUsingStaticIp);
        }

        // Send event to CM & network change broadcast
        setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);

        // 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 fail and the device to disconnect.
        clearTargetBssid("ObtainingIpAddress");

        // Stop IpClient in case we're switching from DHCP to static
        // configuration or vice versa.
        //
        // TODO: Only ever enter this state the first time we connect to a
        // network, never on switching between static configuration and
        // DHCP. When we transition from static configuration to DHCP in
        // particular, we must tell ConnectivityService that we're
        // disconnected, because DHCP might take a long time during which
        // connectivity APIs such as getActiveNetworkInfo should not return
        // CONNECTED.
        stopIpClient();

        mIpClient.setHttpProxy(currentConfig.getHttpProxy());
        if (!TextUtils.isEmpty(mTcpBufferSizes)) {
            mIpClient.setTcpBufferSizes(mTcpBufferSizes);
        }
        final IpClient.ProvisioningConfiguration prov;
        if (!isUsingStaticIp) {
            prov = IpClient.buildProvisioningConfiguration()
                        .withPreDhcpAction()
                        .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                        .withNetwork(getCurrentNetwork())
                        .withDisplayName(currentConfig.SSID)
                        .withRandomMacAddress()
                        .build();
        } else {
            StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
            prov = IpClient.buildProvisioningConfiguration()
                        .withStaticConfiguration(staticIpConfig)
                        .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                        .withNetwork(getCurrentNetwork())
                        .withDisplayName(currentConfig.SSID)
                        .build();
        }
        mIpClient.startProvisioning(prov);
        // Get Link layer stats so as we get fresh tx packet counters
        getWifiLinkLayerStats();
    }
       

回调IpClient.Callback.onProvisioningSuccess接口。

class IpClientCallback extends IpClient.Callback {
    @Override
    public void onPreDhcpAction() {
        sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION);
    }

    @Override
    public void onPostDhcpAction() {
        sendMessage(DhcpClient.CMD_POST_DHCP_ACTION);
    }

    @Override
    public void onNewDhcpResults(DhcpResults dhcpResults) {
        if (dhcpResults != null) {
            sendMessage(CMD_IPV4_PROVISIONING_SUCCESS, dhcpResults);
        } else {
            sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
            mWifiInjector.getWifiLastResortWatchdog().noteConnectionFailureAndTriggerIfNeeded(
                    getTargetSsid(), mTargetRoamBSSID,
                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
        }
    }

    @Override
    public void onProvisioningSuccess(LinkProperties newLp) {
        mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
        sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
        sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
    }

    ...
}

在onProvisioningSuccess函数中发送消息CMD_IP_CONFIGURATION_SUCCESSFUL。

在L2ConnectedState处理消息CMD_IP_CONFIGURATION_SUCCESSFUL。

case CMD_IP_CONFIGURATION_SUCCESSFUL:
    handleSuccessfulIpConfiguration();
    reportConnectionAttemptEnd(
            WifiMetrics.ConnectionEvent.FAILURE_NONE,
            WifiMetricsProto.ConnectionEvent.HLF_NONE);
    if (getCurrentWifiConfiguration() == null) {
        // The current config may have been removed while we were connecting,
        // trigger a disconnect to clear up state.
        mWifiNative.disconnect(mInterfaceName);
        transitionTo(mDisconnectingState);
    } else {
        sendConnectedState();
        transitionTo(mConnectedState);
    }
    break;

看sendConnectedState。

private void sendConnectedState() {
    // If this network was explicitly selected by the user, evaluate whether to call
    // explicitlySelected() so the system can treat it appropriately.
    WifiConfiguration config = getCurrentWifiConfiguration();
    if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
        boolean prompt =
                mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid);
        if (mVerboseLoggingEnabled) {
            log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
        }
        if (prompt) {
            // Selected by the user via Settings or QuickSettings. If this network has Internet
            // access, switch to it. Otherwise, switch to it only if the user confirms that they
            // really want to switch, or has already confirmed and selected "Don't ask again".
            if (mVerboseLoggingEnabled) {
                log("explictlySelected acceptUnvalidated=" + config.noInternetAccessExpected);
            }
            if (mNetworkAgent != null) {
                mNetworkAgent.explicitlySelected(config.noInternetAccessExpected);
            }
        }
    }

    setNetworkDetailedState(DetailedState.CONNECTED);
    sendNetworkStateChangeBroadcast(mLastBssid);
}

将NetworkInfo状态置为CONNECTED状态setNetworkDetailedState(DetailedState.CONNECTED);

并调用sendNetworkStateChangeBroadcast(mLastBssid)

private void sendNetworkStateChangeBroadcast(String bssid) {
    Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
    networkInfo.setExtraInfo(null);
    intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
    //TODO(b/69974497) This should be non-sticky, but settings needs fixing first.
    mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

6 frameworks/base/frameworks/base/services/net/java/android/net/ip/IpClient.java

下面就是前面回调的接口。

/**
* Callbacks for handling IpClient events.
*
* These methods are called by IpClient on its own thread. Implementations
* of this class MUST NOT carry out long-running computations or hold locks
* for which there might be contention with other code calling public
* methods of the same IpClient instance.
*/
public static class Callback {
    // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
    // when constructing a ProvisioningConfiguration.
    //
    // Implementations of onPreDhcpAction() must call
    // IpClient#completedPreDhcpAction() to indicate that DHCP is clear
    // to proceed.
    public void onPreDhcpAction() {}
    public void onPostDhcpAction() {}

    // This is purely advisory and not an indication of provisioning
    // success or failure.  This is only here for callers that want to
    // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
    // DHCPv4 or static IPv4 configuration failure or success can be
    // determined by whether or not the passed-in DhcpResults object is
    // null or not.
    public void onNewDhcpResults(DhcpResults dhcpResults) {}

    public void onProvisioningSuccess(LinkProperties newLp) {}
    public void onProvisioningFailure(LinkProperties newLp) {}

    // Invoked on LinkProperties changes.
    public void onLinkPropertiesChange(LinkProperties newLp) {}

    // Called when the internal IpReachabilityMonitor (if enabled) has
    // detected the loss of a critical number of required neighbors.
    public void onReachabilityLost(String logMsg) {}

    // Called when the IpClient state machine terminates.
    public void onQuit() {}

    // Install an APF program to filter incoming packets.
    public void installPacketFilter(byte[] filter) {}

    // Asynchronously read back the APF program & data buffer from the wifi driver.
    // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
    // buffer. In response to this request, the driver returns the data buffer asynchronously
    // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
    public void startReadPacketFilter() {}

    // If multicast filtering cannot be accomplished with APF, this function will be called to
    // actuate multicast filtering using another means.
    public void setFallbackMulticastFilter(boolean enabled) {}

    // Enabled/disable Neighbor Discover offload functionality. This is
    // called, for example, whenever 464xlat is being started or stopped.
    public void setNeighborDiscoveryOffload(boolean enable) {}
}

猜你喜欢

转载自blog.csdn.net/weixin_42093428/article/details/83066574
今日推荐