Android DHCP 代码流程

关于DHCP协议和对应的包,网上一搜很多,这里就不记录了,大致看下代码流程,代码选取 Android 13 的。

Android 13 ClientModeImpl 类里面的状态机如下:

865          addState(mConnectableState); {
866              addState(mConnectingOrConnectedState, mConnectableState); {
867                  addState(mL2ConnectingState, mConnectingOrConnectedState);
868                  addState(mL2ConnectedState, mConnectingOrConnectedState); {
869                      addState(mL3ProvisioningState, mL2ConnectedState);
870                      addState(mL3ConnectedState, mL2ConnectedState);
871                      addState(mRoamingState, mL2ConnectedState);
872                  }
873              }
874              addState(mDisconnectedState, mConnectableState);
875          }
876  
877          setInitialState(mDisconnectedState);

状态机层次关系如下,所有的父状态是 mConnectableState:
在这里插入图片描述
那什么时候回执行到 ClientModeImpl 类里面呢?这时候我们要回看下之前写的文章,开启 Wi-Fi 流程
这里面提到,打开Wi-Fi,底层初始化完成后,在 /packages/modules/Wifi/service/java/com/android/server/wifi/ConcreteClientModeManager.java里面的ConnectModeState 状态机中,去创建 ClientMode,其实我们只要知道ConcreteClientModeManager里面两种模式即可:Client Mode 和 Scan Only Mode,对应的类就是ClientModeImpl 和 ScanOnlyModeImpl,在回看下创建 ClientMode 代码:
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Wifi/service/java/com/android/server/wifi/
ConcreteClientModeManager.java#makeClientModeImpl

1031          private class ConnectModeState extends State {
1032              @Override
1033              public void enter() {
1034                  Log.d(getTag(), "entering ConnectModeState, starting ClientModeImpl");
1035                  if (mClientInterfaceName == null) {
1036                      Log.e(getTag(), "Supposed to start ClientModeImpl, but iface is null!");
1037                  } else {
1038                      if (mClientModeImpl != null) {
1039                          Log.e(getTag(), "ConnectModeState.enter(): mClientModeImpl is already "
1040                                  + "instantiated?!");
1041                      }
1042                      mClientModeImpl = mWifiInjector.makeClientModeImpl(         ---> 这个就是创建 Client Mode的地方
1043                              mClientInterfaceName, ConcreteClientModeManager.this,
1044                              mVerboseLoggingEnabled);
1045                      mClientModeImpl.setShouldReduceNetworkScore(mShouldReduceNetworkScore);
1046                  }
1047                  if (mConnectRoleChangeInfoToSetOnTransition == null
1048                          || !(mConnectRoleChangeInfoToSetOnTransition.role
1049                          instanceof ClientConnectivityRole)) {
1050                      Log.wtf(TAG, "Unexpected mConnectRoleChangeInfoToSetOnTransition: "
1051                              + mConnectRoleChangeInfoToSetOnTransition);
1052                      // Should never happen, but fallback to primary to avoid a crash.
1053                      mConnectRoleChangeInfoToSetOnTransition =
1054                              new RoleChangeInfo(ROLE_CLIENT_PRIMARY);
1055                  }
1056  
1057                  // Could be any one of possible connect mode roles.
1058                  setRoleInternalAndInvokeCallback(mConnectRoleChangeInfoToSetOnTransition);
1059                  updateConnectModeState(mConnectRoleChangeInfoToSetOnTransition.role,
1060                          WIFI_STATE_ENABLED, WIFI_STATE_ENABLING);
1061              }

Ok,再回到 ClientModeImpl 类里面,父状态ConnectableState :

class ConnectableState extends State {
    @Override
    public void enter() {
        Log.d(getTag(), "entering ConnectableState: ifaceName = " + mInterfaceName);

        setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
        mWifiStateTracker.updateState(mInterfaceName, WifiStateTracker.INVALID);
        mIpClientCallbacks = new IpClientCallbacksImpl();
        Log.d(getTag(), "Start makeIpClient ifaceName = " + mInterfaceName);
        mFacade.makeIpClient(mContext, mInterfaceName, mIpClientCallbacks);  ---> 创建 IpClient,并注册一个mIpClientCallbacks
        mIpClientCallbacks.awaitCreation();   ---> 2s
    }
看下 makeIpClient 流程:
// packages/modules/Wifi/service/java/com/android/server/wifi/FrameworkFacade.java#220
public void makeIpClient(Context context, String iface, IpClientCallbacks callback) {
	IpClientUtil.makeIpClient(context, iface, callback);
}
// packages/modules/NetworkStack/common/networkstackclient/src/android/net/ip/IpClientUtil.java#79
public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
	ModuleNetworkStackClient.getInstance(context).makeIpClient(ifName, new IpClientCallbacksProxy(callback));   ---> callback部分代码未贴出,这里我们先关心makeIpClient流程

ModuleNetworkStackClient父类NetworkStackClientBase中 makeIpClient:
69      public void makeIpClient(String ifName, IIpClientCallbacks cb) {
70          requestConnector(connector -> {
71              try {
72                  connector.makeIpClient(ifName, cb);    ---> 实现是 NetworkStackConnector,所以看这里的代码
73              } catch (RemoteException e) {
74                  throw new IllegalStateException("Could not create IpClient", e);
75              }
76          });
77      }
}
// packages/modules/NetworkStack/src/com/android/server/NetworkStackService.java#makeIpClient
376          @Override
377          public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException {
378              mPermChecker.enforceNetworkStackCallingPermission();
379              updateNetworkStackAidlVersion(cb.getInterfaceVersion(), cb.getInterfaceHash());
380              final IpClient ipClient = mDeps.makeIpClient(
381                      mContext, ifName, cb, mObserverRegistry, this);
382  
383              synchronized (mIpClients) {
384                  final Iterator<WeakReference<IpClient>> it = mIpClients.iterator();
385                  while (it.hasNext()) {
386                      final IpClient ipc = it.next().get();
387                      if (ipc == null) {
388                          it.remove();
389                      }
390                  }
391                  mIpClients.add(new WeakReference<>(ipClient));
392              }
393  			// 创建成功后就回掉这个方法:
394              cb.onIpClientCreated(ipClient.makeConnector());
395          }

onIpClientCreated 回调到 ClientMode里面 packages/modules/Wifi/service/java/com/android/server/wifi/
ClientModeImpl.java#onIpClientCreated

993      class IpClientCallbacksImpl extends IpClientCallbacks {
994          private final ConditionVariable mWaitForCreationCv = new ConditionVariable(false);
995          private final ConditionVariable mWaitForStopCv = new ConditionVariable(false);
996  
997          @Override
998          public void onIpClientCreated(IIpClient ipClient) {
999              // IpClient may take a very long time (many minutes) to start at boot time. But after
1000              // that IpClient should start pretty quickly (a few seconds).
1001              // Blocking wait for 5 seconds first (for when the wait is short)
1002              // If IpClient is still not ready after blocking wait, async wait (for when wait is
1003              // long). Will drop all connection requests until IpClient is ready. Other requests
1004              // will still be processed.
1005              sendMessageAtFrontOfQueue(CMD_CONNECTABLE_STATE_SETUP,     ---> 发送消息
1006                      new IpClientManager(ipClient, getName()));
1007              mWaitForCreationCv.open();
1008          }

我们上面刚看到,此时的状态机是在ConnectModeState,那看下处理这个消息的地方:CMD_CONNECTABLE_STATE_SETUP
3915                  case CMD_CONNECTABLE_STATE_SETUP:
3916                      if (mIpClient != null) {
3917                          loge("Setup connectable state again when IpClient is ready?");
3918                      } else {
3919                          IpClientManager ipClientManager = (IpClientManager) message.obj;
3920                          continueEnterSetup(ipClientManager);    ---> 看这个
3921                      }
3922                      break;

3865          private void continueEnterSetup(IpClientManager ipClientManager) {
3866              mIpClient = ipClientManager;
3867              setupClientMode();    ---》 做一些初始化工作,具体的可以看下代码
3868  
3869              IntentFilter filter = new IntentFilter();
3870              filter.addAction(Intent.ACTION_SCREEN_ON);
3871              filter.addAction(Intent.ACTION_SCREEN_OFF);
3872              if (!mIsScreenStateChangeReceiverRegistered) {
3873                  mContext.registerReceiver(mScreenStateChangeReceiver, filter);
3874                  mIsScreenStateChangeReceiverRegistered = true;
3875              }
3876              // Learn the initial state of whether the screen is on.
3877              // We update this field when we receive broadcasts from the system.
3878              handleScreenStateChanged(mContext.getSystemService(PowerManager.class).isInteractive());
3879  
3880              if (!mWifiNative.removeAllNetworks(mInterfaceName)) {
3881                  loge("Failed to remove networks on entering connect mode");
3882              }
3883              mWifiInfo.reset();
3884              mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
3885  
3886              sendNetworkChangeBroadcast(DetailedState.DISCONNECTED);
3887  
3888              // Inform metrics that Wifi is Enabled (but not yet connected)
3889              mWifiMetrics.setWifiState(mInterfaceName, WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
3890              mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_WIFI_ENABLED);
3891              mWifiScoreCard.noteSupplicantStateChanged(mWifiInfo);
3892          }

OK,接下来就是连接某个界面上的AP,ClientModeImpl类里面的流程还是差不多的,调用 connectNetwork —> 发送 CMD_CONNECT_NETWORK消息 —> connectToUserSelectNetwork —> startConnectToNetwork —> 发送CMD_START_CONNECT —> connectToNetwork(注意看下这个方法,因为这里把状态切到L2ConnectingState,而L2ConnectingState的父状态是ConnectingOrConnectedState,接下来我们会在这个状态里面处理连接成功上报的事件)。那我们直接看连接后分配Ip的相关代码流程,当收到 WifiMonitor.NETWORK_CONNECTION_EVENT 事件表示连接成功(即wpa已经CTRL-EVENT-CONNECTED),父状态处理 ConnectingOrConnectedState里面处理,主要是注意下这个 mWifiConfigManager.addOrUpdateNetwork干了啥即可(如果是新网络则添加,如果是存在过的,则比对,不同则更新),然后 transitionTo(mL3ProvisioningState)状态里,而从上面的状态图我们可以知道L3ProvisioningState的父状态是 L2ConnectedState,所以先看 L2ConnectedState的enter方法:

class L2ConnectedState extends State {
    @Override
    public void enter() {
        mRssiPollToken++;
        if (mEnableRssiPolling) {
            if (isPrimary()) {
                mLinkProbeManager.resetOnNewConnection();
            }
            sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
        }
        sendNetworkChangeBroadcast(DetailedState.CONNECTING);

        // If this network was explicitly selected by the user, evaluate whether to inform
        // ConnectivityService of that fact so the system can treat it appropriately.
        final WifiConfiguration config = getConnectedWifiConfigurationInternal();

        final NetworkAgentConfig naConfig = getNetworkAgentConfigInternal(config);
        final NetworkCapabilities nc = getCapabilities(
                getConnectedWifiConfigurationInternal(), getConnectedBssidInternal());
        // This should never happen.
        if (mNetworkAgent != null) {
            Log.wtf(getTag(), "mNetworkAgent is not null: " + mNetworkAgent);
            mNetworkAgent.unregister();
        }
        mNetworkAgent = mWifiInjector.makeWifiNetworkAgent(nc, mLinkProperties, naConfig,
                mNetworkFactory.getProvider(), new WifiNetworkAgentCallback());
        mWifiScoreReport.setNetworkAgent(mNetworkAgent);
        if (SdkLevel.isAtLeastT()) {
            mQosPolicyRequestHandler.setNetworkAgent(mNetworkAgent);
        }

        // 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");
        mWifiMetrics.setWifiState(mInterfaceName, WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
        mWifiScoreCard.noteNetworkAgentCreated(mWifiInfo,
                mNetworkAgent.getNetwork().getNetId());
        mWifiBlocklistMonitor.handleBssidConnectionSuccess(mLastBssid, mWifiInfo.getSSID());
        // too many places to record connection failure with too many failure reasons.
        // So only record success here.
        mWifiMetrics.noteFirstL2ConnectionAfterBoot(true);
        mCmiMonitor.onL2Connected(mClientModeManager);
        mIsLinkedNetworkRoaming = false;
    }
// 上面父状态执行完,我们再看L3ProvisioningState状态的enter方法:
class L3ProvisioningState extends State {
    @Override
    public void enter() {
        if (mInsecureEapNetworkHandler.startUserApprovalIfNecessary(mIsUserSelected)) {
            return;
        }
        startL3Provisioning();  ---> 看这个方法
    }
    private void startL3Provisioning() {
     WifiConfiguration currentConfig = getConnectedWifiConfigurationInternal();
     if (mIpClientWithPreConnection && mIpClient != null) {
         mIpClient.notifyPreconnectionComplete(mSentHLPs);
         mIpClientWithPreConnection = false;
         mSentHLPs = false;
     } else {
         startIpClient(currentConfig, false);     ---> 启动IpClient,接着看这个
     }
     // Get Link layer stats so as we get fresh tx packet counters
     getWifiLinkLayerStats();
 }
// 看下 startIpClient 的实现:
private boolean startIpClient(WifiConfiguration config, boolean isFilsConnection) {
    if (mIpClient == null || config == null) {
        return false;
    }
	// 是否使用静态ip、随机mac等
    final boolean isUsingStaticIp = (config.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
    final boolean isUsingMacRandomization =
            config.macRandomizationSetting
                    != WifiConfiguration.RANDOMIZATION_NONE
                    && mWifiGlobals.isConnectedMacRandomizationEnabled();
    final List<byte[]> ouis = getOuiInternal(config);
    final List<android.net.DhcpOption> options =
            mWifiConfigManager.getCustomDhcpOptions(WifiSsid.fromString(config.SSID), ouis);
    if (mVerboseLoggingEnabled) {
        final String key = config.getProfileKey();
        log("startIpClient netId=" + Integer.toString(mLastNetworkId)
                + " " + key + " "
                + " roam=" + mIsAutoRoaming
                + " static=" + isUsingStaticIp
                + " randomMac=" + isUsingMacRandomization
                + " isFilsConnection=" + isFilsConnection);
    }

    final MacAddress currentBssid = getCurrentBssidInternalMacAddress();
    final String l2Key = mLastL2KeyAndGroupHint != null
            ? mLastL2KeyAndGroupHint.first : null;
    final String groupHint = mLastL2KeyAndGroupHint != null
            ? mLastL2KeyAndGroupHint.second : null;
    final Layer2Information layer2Info = new Layer2Information(l2Key, groupHint,
            currentBssid);

    if (isFilsConnection) {   --->false,不走这个条件
        stopIpClient();
        if (isUsingStaticIp) {
            mWifiNative.flushAllHlp(mInterfaceName);
            return false;
        }
        setConfigurationsPriorToIpClientProvisioning(config);
        final ProvisioningConfiguration.Builder prov =
                new ProvisioningConfiguration.Builder()
                .withPreDhcpAction()
                .withPreconnection()
                .withDisplayName(config.SSID)
                .withLayer2Information(layer2Info);
        if (mContext.getResources().getBoolean(R.bool.config_wifiEnableApfOnNonPrimarySta)
                || isPrimary()) {
            // unclear if the native layer will return the correct non-capabilities if APF is
            // not supported on secondary interfaces.
            prov.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName));
        }
        if (isUsingMacRandomization) {
            // Use EUI64 address generation for link-local IPv6 addresses.
            prov.withRandomMacAddress();
        }
        prov.withDhcpOptions(convertToInternalDhcpOptions(options));
        mIpClient.startProvisioning(prov.build());
    } else {
    	// 主要是发送这个广播 NETWORK_STATE_CHANGED_ACTION,携带状态
        sendNetworkChangeBroadcast(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.
        //
        // 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.
        stopDhcpSetup();   ---> 启动前先停用掉,做一个重置动作
        setConfigurationsPriorToIpClientProvisioning(config);
        ScanResult scanResult = getScanResultInternal(config);

        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) {    ---> 不使用静态Ip
            prov = new ProvisioningConfiguration.Builder()
                .withPreDhcpAction()      ---> 18s 超时
                .withNetwork(getCurrentNetwork())   ---> 用户选择的网络
                .withDisplayName(config.SSID)   ---> 使用名字
                .withScanResultInfo(scanResultInfo)
                .withLayer2Information(layer2Info);
        } else {
            StaticIpConfiguration staticIpConfig = config.getStaticIpConfiguration();
            prov = new ProvisioningConfiguration.Builder()
                    .withStaticConfiguration(staticIpConfig)
                    .withNetwork(getCurrentNetwork())
                    .withDisplayName(config.SSID)
                    .withLayer2Information(layer2Info);
        }
        // APF 过滤器配置
        if (mContext.getResources().getBoolean(R.bool.config_wifiEnableApfOnNonPrimarySta)
                || isPrimary()) {
            // unclear if the native layer will return the correct non-capabilities if APF is
            // not supported on secondary interfaces.
            prov.withApfCapabilities(mWifiNative.getApfCapabilities(mInterfaceName));
        }
        if (isUsingMacRandomization) {
            // Use EUI64 address generation for link-local IPv6 addresses.
            prov.withRandomMacAddress();
        }
        prov.withDhcpOptions(convertToInternalDhcpOptions(options));   ---> 转换下
        mIpClient.startProvisioning(prov.build());   ---> 主要看这个
    }
    return true;
}

继续看下这个 startProvisioning做了啥:
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/NetworkStack/src/android/net/ip/IpClient.java#819

public void startProvisioning(ProvisioningConfiguration req) {
    if (!req.isValid()) {
        doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
        return;
    }

    mCurrentBssid = getInitialBssid(req.mLayer2Info, req.mScanResultInfo, ShimUtils.isAtLeastS());
    if (req.mLayer2Info != null) {
        mL2Key = req.mLayer2Info.mL2Key;
        mCluster = req.mLayer2Info.mCluster;
    }
    sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
}
// 先看下状态机对应层级
addState(mStoppedState);
addState(mStartedState);
    addState(mPreconnectingState, mStartedState);
    addState(mClearingIpAddressesState, mStartedState);
    addState(mRunningState, mStartedState);
addState(mStoppingState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mStoppedState);
// 对应的处理:
case CMD_START:
    mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
    transitionTo(mClearingIpAddressesState);  ---> 父状态是mStartedState
    break;
 
class ClearingIpAddressesState extends State {
   @Override
   public void enter() {
       // Ensure that interface parameters are fetched on the handler thread so they are
       // properly ordered with other events, such as restoring the interface MTU on teardown.
       mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
       if (mInterfaceParams == null) {
           logError("Failed to find InterfaceParams for " + mInterfaceName);
           doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
           deferMessage(obtainMessage(CMD_STOP, DisconnectCode.DC_INTERFACE_NOT_FOUND.getNumber()));
           return;
       }

       mLinkObserver.setInterfaceParams(mInterfaceParams);

       if (readyToProceed()) {   ---》 首次ipv4和v6都是没有的
           deferMessage(obtainMessage(CMD_ADDRESSES_CLEARED));
       } else {
           // Clear all IPv4 and IPv6 before proceeding to RunningState.
           // Clean up any leftover state from an abnormal exit from
           // tethering or during an IpClient restart.
           stopAllIP();
       }
       mCallback.setNeighborDiscoveryOffload(true);
   }
   
  @Override
  public boolean processMessage(Message msg) {
      switch (msg.what) {
          case CMD_ADDRESSES_CLEARED:
              transitionTo(isUsingPreconnection() ? mPreconnectingState : mRunningState);
              break;
// isUsingPreconnection()的实现看下
private boolean isUsingPreconnection() {
     return mConfiguration.mEnablePreconnection && mConfiguration.mStaticIpConfig == null;
 }
// 这里不是静态的,所以肯定返回的是false,那切到的就是 mRunningState 状态
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
        // Resource settings were moved from ApfCapabilities APIs to NetworkStack resources in S
        if (ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.R)) {
            final Resources res = mContext.getResources();
            apfConfig.ieee802_3Filter = res.getBoolean(R.bool.config_apfDrop802_3Frames);
            apfConfig.ethTypeBlackList = res.getIntArray(R.array.config_apfEthTypeDenyList);
        } else {
            apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
            apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
        }

        apfConfig.minRdnssLifetimeSec = mMinRdnssLifetimeSec;
        mApfFilter = mDependencies.maybeCreateApfFilter(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);

        final int acceptRa =
                mConfiguration.mIPv6ProvisioningMode == PROV_IPV6_LINKLOCAL ? 0 : 2;
        if (isIpv6Enabled() && !startIPv6(acceptRa)) {
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
            enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6);
            return;
        }

        if (isIpv4Enabled() && !isUsingPreconnection() && !startIPv4()) {  ---> 看ipv4的获取
            doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
            enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV4);
            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(DisconnectCode.DC_INVALID_PROVISIONING);
            return;
        }

        if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
            doImmediateProvisioningFailure(
                    IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
            enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPREACHABILITYMONITOR);
            return;
        }
    }
// 看ipv4的获取:
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 {
        if (mDhcpClient != null) {
            Log.wtf(mTag, "DhcpClient should never be non-null in startIPv4()");
        }
        startDhcpClient();   ---> 看这个函数
    }
    return true;
}


private void startDhcpClient() {
    // Start DHCPv4.
    mDhcpClient = mDependencies.makeDhcpClient(mContext, IpClient.this, mInterfaceParams,
            mDependencies.getDhcpClientDependencies(mIpMemoryStore, mIpProvisioningMetrics));

    // Check if the vendor-specific IE oui/type matches and filters the customized DHCP options.
    final List<DhcpOption> options = maybeFilterCustomizedDhcpOptions();

    // If preconnection is enabled, there is no need to ask Wi-Fi to disable powersaving
    // during DHCP, because the DHCP handshake will happen during association. In order to
    // ensure that future renews still do the DHCP action (if configured),
    // registerForPreDhcpNotification is called later when processing the CMD_*_PRECONNECTION
    // messages.
    if (!isUsingPreconnection()) mDhcpClient.registerForPreDhcpNotification();
    // 这不就是熟悉的味道了么: DhcpClient.CMD_START_DHCP
    mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP, new DhcpClient.Configuration(mL2Key, isUsingPreconnection(), options));
}

后面的流程用文字总结下就是:IpClient 中发送 CMD_START_DHCP 在DhcpClient 中 StoppedState 里面处理
StoppedState —> WaitBeforeObtainingConfigurationState —> ObtainingConfigurationState —> DhcpInitState —> DhcpRequestingState —> ConfiguringInterfaceState —> DhcpBoundState
其中 DhcpInitState 里面 是处理发包(Broadcasting DHCPDISCOVER)和收包,DhcpRequestingState 里面处理发包(DHCPREQUEST)和收包
到此我们就大致梳理了DHCP的流程,当然文字描述比较简单,但代码实际内容还是挺多的,可以细看下代码。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值