在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的广播。
private class SupplicantVendorStaIfaceHalCallback extends ISupplicantVendorStaIfaceCallback.Stub {private String mIfaceName;private SupplicantStaIfaceHalCallback mSupplicantStaIfacecallback;SupplicantVendorStaIfaceHalCallback(@NonNull String ifaceName, SupplicantStaIfaceHalCallback callback) {mIfaceName = ifaceName;mSupplicantStaIfacecallback = callback;}@Overridepublic 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
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
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 oneconfig.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);} else {// Clear any stored pseudonymsconfig.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。
class ObtainingIpState extends State {@Overridepublic 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 countersgetWifiLinkLayerStats();}
二、接着我们再看startIpClient的具体内容。
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
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
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
class RunningState extends State {private ConnectivityPacketTracker mPacketTracker;private boolean mDhcpActionInFlight;@Overridepublic void enter() {ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;apfConfig.multicastFilter = mMulticastFiltering;// Get the Configuration for ApfFilter from ContextapfConfig.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流程接上了。
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));elsemDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);}return true;}