前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android11 Wifi连接流程之IP地址分配

Android11 Wifi连接流程之IP地址分配

作者头像
用户7557625
发布2021-09-14 14:58:00
2.7K0
发布2021-09-14 14:58:00
举报

Android11 wifi连接流程中我们代码跟踪到了supplicant中开始associate,关联成功以后就是四次握手然后连接成功。连接成功以后还需要分配IP地址,才可以通信,这一节我们看一下IP地址的获取流程。

一、在ClientModeImpl中有一个函数startIpClient。这个函数会在俩个地方被调用,一个是连接的时候ConnectModeState,一个是连接成功以后进入ObtainingIpState。这俩个地方的区别就是isFilsConnection的不同,连接过程中isFilsConnection为true,把IPClinet先关掉。如果isFilsConnection为flase,则开始处理IP地址分配。

frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java

这里我们先看是怎么进入ObtainingIpState的

SupplicantStaIfaceHal中注册了一个Supplicant的回调函数,当supplicant的状态发生改变时这里就会监听到,然后WifiMonitor就会发送statechange的广播。

代码语言:javascript
复制
private class SupplicantVendorStaIfaceHalCallback extends ISupplicantVendorStaIfaceCallback.Stub {
    private String mIfaceName;
    private SupplicantStaIfaceHalCallback mSupplicantStaIfacecallback;

    SupplicantVendorStaIfaceHalCallback(@NonNull String ifaceName, SupplicantStaIfaceHalCallback callback) {
        mIfaceName = ifaceName;
        mSupplicantStaIfacecallback = callback;
    }

    @Override
    public void onVendorStateChanged(int newState, byte[/* 6 */] bssid, int id,
                               ArrayList<Byte> ssid, boolean filsHlpSent) {
        synchronized (mLock) {
            logCallback("onVendorStateChanged");
            SupplicantState newSupplicantState =
                SupplicantStaIfaceCallbackImpl.supplicantHidlStateToFrameworkState(newState);
            WifiSsid wifiSsid = // wifigbk++
                    WifiGbk.createWifiSsidFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
            String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
            if (newSupplicantState == SupplicantState.COMPLETED) {
                mWifiMonitor.broadcastNetworkConnectionEvent(
                        mIfaceName, getCurrentNetworkId(mIfaceName), filsHlpSent, bssidStr);
            }
            mWifiMonitor.broadcastSupplicantStateChangeEvent(
                    mIfaceName, getCurrentNetworkId(mIfaceName), wifiSsid, bssidStr, newSupplicantState);
        }
    }

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

代码语言:javascript
复制
public void broadcastNetworkConnectionEvent(String iface, int networkId, boolean filsHlpSent,
        String bssid) {
    sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, filsHlpSent ? 1 : 0, bssid);
}

此时wifi状态机还在ConnectModeState,对于NETWORK_CONNECTION_EVENT的处理结果就是跳转到ObtainingIpState

代码语言:javascript
复制
case WifiMonitor.NETWORK_CONNECTION_EVENT:
    if (mVerboseLoggingEnabled) log("Network connection established");
    mLastNetworkId = message.arg1;
    mSentHLPs = message.arg2 == 1;
    if (mSentHLPs) mWifiMetrics.incrementL2ConnectionThroughFilsAuthCount();
    mWifiConfigManager.clearRecentFailureReason(mLastNetworkId);
    mLastBssid = (String) message.obj;
    reasonCode = message.arg2;
    // TODO: This check should not be needed after ClientModeImpl 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) {
        if (mWifiConfigManager.saveAutoConnectedNewNetwork(config.networkId)) {
            Log.i(TAG, "Successfully connected to new network " + config.getPrintableSsid());
            mAutoConnectNewNetworkResultNotifier.onConnectionAttemptSuccess(config.SSID);
        }
        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) {
                updateConnectedBand(scanResult.frequency, true);
            }
        }

        // We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
        if (config.enterpriseConfig != null
                && config.enterpriseConfig.isAuthenticationSimBased()) {
            mLastSubId = mWifiCarrierInfoManager.getBestMatchSubscriptionId(config);
            mLastSimBasedConnectionCarrierName =
                mWifiCarrierInfoManager.getCarrierNameforSubId(mLastSubId);
            String anonymousIdentity =
                    mWifiNative.getEapAnonymousIdentity(mInterfaceName);
            if (!TextUtils.isEmpty(anonymousIdentity)
                    && !WifiCarrierInfoManager
                    .isAnonymousAtRealmIdentity(anonymousIdentity)) {
                String decoratedPseudonym = mWifiCarrierInfoManager
                        .decoratePseudonymWith3GppRealm(config,
                                anonymousIdentity);
                if (decoratedPseudonym != null) {
                    anonymousIdentity = decoratedPseudonym;
                }
                if (mVerboseLoggingEnabled) {
                    log("EAP Pseudonym: " + anonymousIdentity);
                }
                // Save the pseudonym only if it is a real one
                config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
            } else {
                // Clear any stored pseudonyms
                config.enterpriseConfig.setAnonymousIdentity(null);
            }
            mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
        }
        mIpReachabilityMonitorActive = true;
        transitionTo(mObtainingIpState);
    } else {
        logw("Connected to unknown networkId " + mLastNetworkId
                + ", disconnecting...");
        sendMessage(CMD_DISCONNECT);
    }

在ObtainingIpState进入时就会开启IPClient,注意这里if (mIpClientWithPreConnection && mIpClient != null) {这个判断条件一定是不成立的,因为在连接时执行过stopIpClient。

代码语言:javascript
复制
class ObtainingIpState extends State {
    @Override
    public void enter() {
        // Reset power save mode after association.
        // Kernel does not forward power save request to driver if power
        // save state of that interface is same as requested state in
        // cfg80211. This happens when driver’s power save state not
        // synchronized with cfg80211 power save state.
        // By resetting power save state resolves issues of cfg80211
        // ignoring enable power save request sent in ObtainingIpState.
        mWifiNative.setPowerSave(mInterfaceName, false);

        WifiConfiguration currentConfig = getCurrentWifiConfiguration();
        if (mIpClientWithPreConnection && mIpClient != null) {
            mIpClient.notifyPreconnectionComplete(mSentHLPs);
            mIpClientWithPreConnection = false;
            mSentHLPs = false;
        } else {
            startIpClient(currentConfig, false);
        }
        // Get Link layer stats so as we get fresh tx packet counters
        getWifiLinkLayerStats();
    }

二、接着我们再看startIpClient的具体内容。

代码语言:javascript
复制
private boolean startIpClient(WifiConfiguration config, boolean isFilsConnection) {
    final boolean isUsingStaticIp =
            (config.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
    final boolean isUsingMacRandomization =
            config.macRandomizationSetting
                    == WifiConfiguration.RANDOMIZATION_PERSISTENT
                    && isConnectedMacRandomizationEnabled();
    if (isFilsConnection) {
        stopIpClient();
        if (isUsingStaticIp) {
            mWifiNative.flushAllHlp(mInterfaceName);
            return false;
        }
        setConfigurationsPriorToIpClientProvisioning(config);
        final ProvisioningConfiguration.Builder prov =
                new ProvisioningConfiguration.Builder()
                .withPreDhcpAction()
                .withPreconnection()
                .withApfCapabilities(
                mWifiNative.getApfCapabilities(mInterfaceName))
                .withLayer2Information(layer2Info);
        if (isUsingMacRandomization) {
            // Use EUI64 address generation for link-local IPv6 addresses.
            prov.withRandomMacAddress();
        }
        mIpClient.startProvisioning(prov.build());
    } else {
        sendNetworkChangeBroadcast(DetailedState.OBTAINING_IPADDR);
        clearTargetBssid("ObtainingIpAddress");
        stopDhcpSetup();
        setConfigurationsPriorToIpClientProvisioning(config);
        ScanDetailCache scanDetailCache =
                mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
        ScanResult scanResult = null;
        if (mLastBssid != null) {
            if (scanDetailCache != null) {
                scanResult = scanDetailCache.getScanResult(mLastBssid);
            }
            if (scanResult == null) {
                ScanRequestProxy scanRequestProxy = mWifiInjector.getScanRequestProxy();
                List<ScanResult> scanResults = scanRequestProxy.getScanResults();
                for (ScanResult result : scanResults) {
                    if (result.SSID.equals(WifiInfo.removeDoubleQuotes(config.SSID))
                            && result.BSSID.equals(mLastBssid)) {
                        scanResult = result;
                        break;
                    }
                }
            }
        }
        final ProvisioningConfiguration.Builder prov;
        ProvisioningConfiguration.ScanResultInfo scanResultInfo = null;
        if (scanResult != null) {
            final List<ScanResultInfo.InformationElement> ies =
                    new ArrayList<ScanResultInfo.InformationElement>();
            for (ScanResult.InformationElement ie : scanResult.getInformationElements()) {
                ScanResultInfo.InformationElement scanResultInfoIe =
                        new ScanResultInfo.InformationElement(ie.getId(), ie.getBytes());
                ies.add(scanResultInfoIe);
            }
            scanResultInfo = new ProvisioningConfiguration.ScanResultInfo(scanResult.SSID,
                    scanResult.BSSID, ies);
        }

        if (!isUsingStaticIp) {
            prov = new ProvisioningConfiguration.Builder()
                .withPreDhcpAction()
                .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                .withNetwork(getCurrentNetwork())
                .withDisplayName(config.SSID)
                .withScanResultInfo(scanResultInfo)
                .withLayer2Information(layer2Info);
        } else {
            StaticIpConfiguration staticIpConfig = config.getStaticIpConfiguration();
            prov = new ProvisioningConfiguration.Builder()
                    .withStaticConfiguration(staticIpConfig)
                    .withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName))
                    .withNetwork(getCurrentNetwork())
                    .withDisplayName(config.SSID)
                    .withLayer2Information(layer2Info);
        }
        if (isUsingMacRandomization) {
            // Use EUI64 address generation for link-local IPv6 addresses.
            prov.withRandomMacAddress();
        }
        mIpClient.startProvisioning(prov.build());
    }

    return true;
}

三、IpClientManager通过aidl与IPClinet模块通信。

frameworks/base/services/net/java/android/net/ip/IpClientManager.java

代码语言:javascript
复制
public class IpClientManager {
    @NonNull private final IIpClient mIpClient;
    @NonNull private final String mTag;

    public IpClientManager(@NonNull IIpClient ipClient, @NonNull String tag) {
        mIpClient = ipClient;
        mTag = tag;
    }

IPClinet会发送CMD_START信息,然后会进入StartedState。

frameworks/base/packages/NetworkStack/src/android/net/ip/IpClient.java

代码语言:javascript
复制
public void startProvisioning(ProvisioningConfiguration req) {
    if (!req.isValid()) {
        doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
        return;
    }

    mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
    if (mInterfaceParams == null) {
        logError("Failed to find InterfaceParams for " + mInterfaceName);
        doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
        return;
    }

    mCallback.setNeighborDiscoveryOffload(true);
    sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
}

最后进入了RunningState。在这里会开始Ipv6和Ipv4

代码语言:javascript
复制
class RunningState extends State {
    private ConnectivityPacketTracker mPacketTracker;
    private boolean mDhcpActionInFlight;

    @Override
    public void enter() {
        ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
        apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
        apfConfig.multicastFilter = mMulticastFiltering;
        // Get the Configuration for ApfFilter from Context
        apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
        apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
        mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
        // TODO: investigate the effects of any multicast filtering racing/interfering with the
        // rest of this IP configuration startup.
        if (mApfFilter == null) {
            mCallback.setFallbackMulticastFilter(mMulticastFiltering);
        }

        mPacketTracker = createPacketTracker();
        if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);

        if (mConfiguration.mEnableIPv6 && !startIPv6()) {
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
            enqueueJumpToStoppingState();
            return;
        }

        if (mConfiguration.mEnableIPv4 && !startIPv4()) {
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
            enqueueJumpToStoppingState();
            return;
        }

        final InitialConfiguration config = mConfiguration.mInitialConfig;
        if ((config != null) && !applyInitialConfig(config)) {
            // TODO introduce a new IpManagerEvent constant to distinguish this error case.
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
            enqueueJumpToStoppingState();
            return;
        }

        if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
            doImmediateProvisioningFailure(
                    IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
            enqueueJumpToStoppingState();
            return;
        }
    }

这里会发送广播CMD_START_DHCP给DHCPClinet。到了这一步就和Android11 DHCP流程接上了。

代码语言:javascript
复制
    private boolean startIPv4() {
        // If we have a StaticIpConfiguration attempt to apply it and
        // handle the result accordingly.
        if (mConfiguration.mStaticIpConfig != null) {
            if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) {
                handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
            } else {
                return false;
            }
        } else {
            // Start DHCPv4.
            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
            mDhcpClient.registerForPreDhcpNotification();
            if (mConfiguration.mRapidCommit || mConfiguration.mDiscoverSent)
                mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP_RAPID_COMMIT,
                    (mConfiguration.mRapidCommit ? 1: 0),
                    (mConfiguration.mDiscoverSent ? 1: 0));
            else
                mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
        }

        return true;
    }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档