1. 程式人生 > >Android N Wi-Fi 啟動流程分析

Android N Wi-Fi 啟動流程分析

當我們在手機的setting中開啟wifi開關時,首先是wifisetting中的oncreate將載入佈局檔案。
 @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        addPreferencesFromResource(R.xml.wifi_settings);//載入檔案佈局
        mAddPreference = new Preference(getContext());
        mAddPreference.setIcon(R.drawable.ic_menu_add_inset);
        mAddPreference.setTitle(R.string.wifi_add_network);

        mUserBadgeCache = new
AccessPointPreference.UserBadgeCache(getPackageManager()); mBgThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); mBgThread.start(); mWifiSettingsExt = new WifiSettingsExt(getActivity()); mWifiSettingsExt.onCreate(); }
接下來在onStart中將建立 WifiEnabler
@Override
    public void onStart() {
        super.onStart();

        // On/off switch is hidden for Setup Wizard (returns null)
        mWifiEnabler = createWifiEnabler();
    }
    /**
     * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
     */
    /* package */ WifiEnabler createWifiEnabler() {
        final
SettingsActivity activity = (SettingsActivity) getActivity(); return new WifiEnabler(activity, activity.getSwitchBar()); }
WifiEnabler 建構函式中,將獲得 WifiManager,並且新增廣播監聽事件,接下來顯示 switchBar, 同時將註冊switchBar的監聽事件。
public WifiEnabler(Context context, SwitchBar switchBar) {
        mContext = context;
        mSwitchBar = switchBar;

        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

        mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
        // The order matters! We really should not depend on this. :(
        mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

        setupSwitchBar();
    }

    public void setupSwitchBar() {
        final int state = mWifiManager.getWifiState();
        handleWifiStateChanged(state);
        if (!mListeningToOnSwitchChange) {
            mSwitchBar.addOnSwitchChangeListener(this);
            mListeningToOnSwitchChange = true;
        }
        mSwitchBar.show();
    }
當我們開啟wifi開關的時候,SwitchBar監聽事件將觸發。此時將執行mWifiManager.setWifiEnabled(isChecked)。
 @Override
    public void onSwitchChanged(Switch switchView, boolean isChecked) {
        Log.d(TAG, "onCheckedChanged, isChecked = " + isChecked);
        //Do nothing if called as a result of a state machine event
        if (mStateMachineEvent) {
            return;
        }
        // Show toast message if Wi-Fi is not allowed in airplane mode
        if (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {
            Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
            // Reset switch to off. No infinite check/listenenr loop.
            mSwitchBar.setChecked(false);
            return;
        }

        // Disable tethering if enabling Wifi
        if (mayDisableTethering(isChecked)) {
            mWifiManager.setWifiApEnabled(null, false);
        }

        Log.d(TAG, "onCheckedChanged, setWifiEnabled = " + isChecked);
        MetricsLogger.action(mContext,
                isChecked ? MetricsEvent.ACTION_WIFI_ON : MetricsEvent.ACTION_WIFI_OFF);
        if (!mWifiManager.setWifiEnabled(isChecked)) {
            // 開啟wifi開關
            mSwitchBar.setEnabled(true);
            Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
        }
    }
WifiManager中將直接呼叫 WifiServiceImpl中的同名函式,此時WifiManager與WifiService將通過adil進行非同步通訊。
     /**
     * Enable or disable Wi-Fi.
     * @param enabled {@code true} to enable, {@code false} to disable.
     * @return {@code true} if the operation succeeds (or if the existing state
     *         is the same as the requested state).
     */
    public boolean setWifiEnabled(boolean enabled) {
        try {
            return mService.setWifiEnabled(enabled);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
WifiServiceImpl 中的setWifiEnabled 將向 WifiController 中傳送 CMD_WIFI_TOGGLED 訊息。 
    /**
     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
     * @param enable {@code true} to enable, {@code false} to disable.
     * @return {@code true} if the enable/disable operation was
     *         started or is already in the queue.
     */
    public synchronized boolean setWifiEnabled(boolean enable) {
        enforceChangePermission();
        /*
        * Caller might not have WRITE_SECURE_SETTINGS,
        * only CHANGE_WIFI_STATE is enforced
        */

        long ident = Binder.clearCallingIdentity();
        try {
            if (! mSettingsStore.handleWifiToggled(enable)) {
                // Nothing to do if wifi cannot be toggled
                return true;
            }
            //Added by [email protected] for defect 4640139 2017-06-09 begin
            if (enable) {
                if (mCm == null) {
                    mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
                }
                mCm.stopTethering(ConnectivityManager.TETHERING_USB);
            }
            //Added by [email protected] for defect 4640139 2017-06-09 end
        } finally {
            Binder.restoreCallingIdentity(ident);
        }

        ///M: put extra mWifiIpoOff
        mWifiController.obtainMessage(CMD_WIFI_TOGGLED,
                mWifiIpoOff ? WifiController.WIFI_TOGGLED_IPOOFF : 0,
                Binder.getCallingUid()).sendToTarget();
        return true;
    }
WifiController也是一個狀態機,它將向 WifiStateMachine 中傳送三條訊息。分別是:

1. WifiStateMachine.setSupplicantRunning(true):傳送CMD_START_SUPPLICANT訊息
2. WifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE)
傳送CMD_SET_OPERATIONAL_MODE訊息,引數是CONNECT_MODE
3. WifiStateMachine.setDriverStart(true):傳送CMD_START_DRIVER訊息

接下來我們來到 WifiStateMachine 中,此時它已經完成了一些列初始化,並且處於 InitialState 狀態中,
InitialState 的processMessage 將處理第一條訊息 CMD_START_SUPPLICANT 。

case CMD_START_SUPPLICANT:
                    if (mWifiNative.loadDriver()) {//載入驅動
                        try {
                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");
                        } catch (Exception e) {
                            loge("Failed to reload STA firmware " + e);
                            ///M: ALPS02530654 Let wifi chip to recover @{
                            loge("fwreload fail, unloadDriver");
                            mWifiNative.unloadDriver();
                            ///@}
                            ///M: add the following @{
                            //throw exception to highlight wifi error
                            ExceptionLog exceptionLog = null;
                            setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
                            return HANDLED;
                        }

                        try {
                            // A runtime crash can leave the interface up and
                            // IP addresses configured, and this affects
                            // connectivity when supplicant starts up.
                            // Ensure interface is down and we have no IP
                            // addresses before a supplicant start.
                            mNwService.setInterfaceDown(mInterfaceName);
                            mNwService.clearInterfaceAddresses(mInterfaceName);

                            // Set privacy extensions
                           mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
                            mNwService.disableIpv6(mInterfaceName);
                        } catch (RemoteException re) {
                            loge("Unable to change interface settings: " + re);
                        } catch (IllegalStateException ie) {
                            loge("Unable to change interface settings: " + ie);
                        }

                       /* Stop a running supplicant after a runtime restart
                        * Avoids issues with drivers that do not handle interface down
                        * on a running supplicant properly.
                        */
                        mWifiMonitor.killSupplicant(mP2pSupported);

                        if (mWifiNative.startHal() == false) {
                            /* starting HAL is optional */
                            loge("Failed to start HAL");
                        }

                        if (mWifiNative.startSupplicant(mP2pSupported)) {
                            setWifiState(WIFI_STATE_ENABLING);
                            if (DBG) log("Supplicant start successful");
                            mWifiMonitor.startMonitoring(mInterfaceName);
                            transitionTo(mSupplicantStartingState);
                        } else {
                            loge("Failed to start supplicant!");
                            setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
                            ///M: add the following @{
                            //throw exception to highlight wifi error
                            ExceptionLog exceptionLog = null;
                        }
                    } else {
                        loge("Failed to load driver");
                        ///M: add the following @{
                        //throw exception to highlight wifi error
                        ExceptionLog exceptionLog = null;
                        setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
                    }
                    break;
這裡主要是載入wifi的驅動,以及 WifiNative.startSupplicant 連線wpas,連線成功後,
函式setWifiState(WIFI_STATE_ENABLING)將傳送 WifiManager.WIFI_STATE_CHANGED_ACTION 廣播;
然後啟動startMonitoring(mInterfaceName),WifiMonitor與wpas建立聯絡後,將傳送SUP_CONNECTION_EVENT訊息給 
WifiStateMachine 
最後將狀態切換至 SupplicantStartingState 狀態。

此時 WifiStateMachine 將有三個訊息等待處理,他們分別是:
  1. CMD_SET_OPERATIONAL_MODE
  2. CMD_START_DRIVER
  3. SUP_CONNECTION_EVENT

切換至 SupplicantStartingState 狀態,將執行processMessage函式。

case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_FREQUENCY_BAND:
     messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
     deferMessage(message);
     break;
case CMD_SET_OPERATIONAL_MODE:
     mOperationalMode = message.arg1;
     deferMessage(message);
     break;
可以發現,此時前兩條訊息都被延遲處理了,接下來看第三條訊息。
case WifiMonitor.SUP_CONNECTION_EVENT:
     setWifiState(WIFI_STATE_ENABLED);
     mSupplicantRestartCount = 0;
     /* Reset the supplicant state to indicate the supplicant
      * state is not known at this time */
     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
     /* Initialize data structures */
     mLastBssid = null;
     mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
     mLastSignalLevel = -1;

     mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
     /* set frequency band of operation */
     setFrequencyBand();
     mWifiNative.enableSaveConfig();
     mWifiConfigManager.loadAndEnableAllNetworks();
     if (mWifiConfigManager.mEnableVerboseLogging.get() > 0) {
         enableVerboseLogging(mWifiConfigManager.mEnableVerboseLogging.get());
     }
     initializeWpsDetails();
     ///M: scan stratagy
     mConnectNetwork = false;

     ///M: For PPPOE
     mLastExplicitNetworkId = INVALID_NETWORK_ID;
     mOnlineStartTime = 0;
     mUsingPppoe = false;

     ///M: for operator plugin @{
     mConnectNetwork = false;
     if (hasCustomizedAutoConnect()) {
         mWifiNative.setBssExpireAge(IWifiFwkExt.BSS_EXPIRE_AGE);
         mWifiNative.setBssExpireCount(IWifiFwkExt.BSS_EXPIRE_COUNT);
         mDisconnectOperation = false;
         mScanForWeakSignal = false;
         mShowReselectDialog = false;
         mIpConfigLost = false;
         mLastCheckWeakSignalTime = 0;
         if (!mWifiFwkExt.shouldAutoConnect()) {
             disableAllNetworks(false);
         }
     }
     ///@}

     ///M: ALPS02317999 Prevent supplicant auto connect EAP SIM/AKA @{
     if (isAirplaneModeOn()) {
         List<WifiConfiguration> networks = mWifiConfigManager.getSavedNetworks();
         if (null != networks) {
             for (WifiConfiguration network : networks) {
                 if (mWifiConfigManager.isSimConfig(network)) {
                     mWifiConfigManager.disableNetwork(network.networkId);
                     ///M: ALPS01975084 airplane mode on,
                     ///   should not auto connect EAP SIM
                     WifiConfiguration eapSimConfig =
                         mWifiConfigManager.getWifiConfiguration(network.networkId);
                     ///M:PPR2 igmore passpoint network
                     //because sim info may not get from sim.
                     if (eapSimConfig.isPasspoint() && mPPR2Support) {
                     } else {
                         mWifiConfigManager.updateNetworkSelectionStatus(
                             eapSimConfig,
                             WifiConfiguration.NetworkSelectionStatus
                             .DISABLED_AUTHENTICATION_SIM_CARD_ABSENT);
                     }
                 }
             }
         } else {
             log("Check for EAP_SIM_AKA, networks is null!");
         }
         ///M: ALPS02319256 Disable EAP SIM/AKA AP if modem is not ready
     } else if (!mSim1IccState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)
             || !mSim2IccState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) {
         log("iccState: (" + mSim1IccState + "," + mSim2IccState
                 + "), check EAP SIM/AKA networks");
         List<WifiConfiguration> networks = mWifiConfigManager.getSavedNetworks();
         if (null != networks) {
             for (WifiConfiguration network : networks) {
                 if (mWifiConfigManager.isSimConfig(network)
                         && !isConfigSimCardLoaded(network)) {
                     log("diable EAP SIM/AKA network let supplicant cannot "
                             + "auto connect, netId: " + network.networkId);
                     mWifiConfigManager.disableNetwork(network.networkId);
                     ///M: ALPS02319256 airplane mode off and wifi on
                     //    modem is not ready, supplicant should not auto connect
                     WifiConfiguration eapSimConfig =
                         mWifiConfigManager.getWifiConfiguration(network.networkId);
                     ///M:PPR2 igmore passpoint network
                     //because sim info may not get from sim.
                     if (eapSimConfig.isPasspoint() && mPPR2Support) {
                     } else {
                         mWifiConfigManager.updateNetworkSelectionStatus(
                                 eapSimConfig,
                                 WifiConfiguration.NetworkSelectionStatus
                                 .DISABLED_AUTHENTICATION_SIM_CARD_ABSENT);
                     }
                 }
             }
         } else {
             log("Check for EAP_SIM_AKA, networks is null!");
         }
     }
     ///@}

     sendSupplicantConnectionChangedBroadcast(true);
     transitionTo(mDriverStartedState);
     break;
    這裡傳送了一個 WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION 廣播,通知上層可以開始掃描了。
    然後切換到 DriverStartedState 狀態。切換到該狀態 需要執行 SupplicantStartedState 和 DriverStartedState 的enter函式。
class SupplicantStartedState extends State {
@Override
 public void enter() {
     if (DBG) loge(getName() + "\n");
     /* Wifi is available as long as we have a connection to supplicant */
     mNetworkInfo.setIsAvailable(true);
     if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);

     int defaultInterval = mContext.getResources().getInteger(
             R.integer.config_wifi_supplicant_scan_interval);

     ///M: for operator plugin @{
     if (hasCustomizedAutoConnect()) {
         mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
                 Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
                 mScreenOn ? defaultInterval : mContext.getResources().getInteger(
                         R.integer.config_wifi_framework_scan_interval));
     } else {
     ///@}
     mSupplicantScanIntervalMs = mFacade.getLongSetting(mContext,
             Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
             defaultInterval);
     ///M: for operator plugin @{
     }
     ///@}

     //Task3202733 BEGIN-leida-Oct.29,2016
     String customMTUSizeStr =    mContext.getResources().getString(com.android.internal.R.string.def_framework_wifi_mtu);
     Log.d(TAG, "custom MTU size is used, size:" + customMTUSizeStr);
     int customMTUSize = Integer.parseInt(customMTUSizeStr);
     Log.d(TAG, "custom MTU size is used, size:" + customMTUSize);
     if (customMTUSize > 0) {
         if (customMTUSize < 1000 || customMTUSize > 1500) {
             Log.d(TAG, "Invalid MTU size of wlan0:" + customMTUSize);
         } else {
             Log.d(TAG, "set MTU size as customizationed:" + customMTUSize);
             try {
                 mNwService.setMtu("wlan0", customMTUSize);
             } catch (Exception e) {
                 Log.d(TAG, "exception in set customization MTU size:" + e);
             }
         }
     } else {
         Log.d(TAG, "set MTU size as default:1500");
         try {
             mNwService.setMtu("wlan0", 1500);
         } catch (Exception e) {
             Log.d(TAG, "exception in set default MTU size:" + e);
         }
     }
     //Task3202733 END-leida-Oct.29,2016

     mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);//設定掃描時間間隔
     mWifiNative.setExternalSim(true);

     /* turn on use of DFS channels */
     mWifiNative.setDfsFlag(true);

     setRandomMacOui();
     mWifiNative.enableAutoConnect(false);
     mCountryCode.setReadyForChange(true);

     ///M: for operator plugin @{
     // if have special network selection and wifi is connected, don't try to associate.
     if (mWifiFwkExt != null && mWifiFwkExt.hasNetworkSelection() != 0) {
         //don't let supplicant auto connect when wifi initial
         mWifiNative.disconnect();
     }
     ///@}
 }

這裡主要是設定一下掃描時間間隔,接下來看看 DriverStartedState 的enter函式。

class DriverStartedState extends State {
      @Override
      public void enter() {
          if (DBG) {
              logd("DriverStartedState enter");
          }

          // We can't do this in the constructor because WifiStateMachine is created before the
          // wifi scanning service is initialized
          if (mWifiScanner == null) {
              mWifiScanner = mFacade.makeWifiScanner(mContext, getHandler().getLooper());

              mWifiConnectivityManager = new WifiConnectivityManager(mContext,
                  WifiStateMachine.this, mWifiScanner, mWifiConfigManager, mWifiInfo,
                  mWifiQualifiedNetworkSelector, mWifiInjector,
                  getHandler().getLooper());
          }

          mWifiLogger.startLogging(DBG);
          mIsRunning = true;
          updateBatteryWorkSource(null);
          /**
           * Enable bluetooth coexistence scan mode when bluetooth connection is active.
           * When this mode is on, some of the low-level scan parameters used by the
           * driver are changed to reduce interference with bluetooth
           */
          mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
          /* initialize network state */
          setNetworkDetailedState(DetailedState.DISCONNECTED);

          // Disable legacy multicast filtering, which on some chipsets defaults to enabled.
          // Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6
          // provisioning. Legacy IPv4 multicast filtering may be re-enabled later via
          // IpManager.Callback.setFallbackMulticastFilter()
          mWifiNative.stopFilteringMulticastV4Packets();
          mWifiNative.stopFilteringMulticastV6Packets();

          if (mOperationalMode != CONNECT_MODE) {//此時mOperationalMode == CONNECT_MODE
              mWifiNative.disconnect();
              mWifiConfigManager.disableAllNetworksNative();
              if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
                  setWifiState(WIFI_STATE_DISABLED);
              }
              transitionTo(mScanModeState);
          } else {

              // Status pulls in the current supplicant state and network connection state
              // events over the monitor connection. This helps framework sync up with
              // current supplicant state
              // TODO: actually check th supplicant status string and make sure the supplicant
              // is in disconnecte4d state.
              mWifiNative.status();

              ///M: ALPS02028415 reset scan count
              mWifiOnScanCount = 0;

              // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
              transitionTo(mDisconnectedState);
              ///M: N eac may be google mistake
              //transitionTo(mDisconnectedState);
          }

          // We may have missed screen update at boot
          if (mScreenBroadcastReceived.get() == false) {
              PowerManager powerManager = (PowerManager)mContext.getSystemService(
                      Context.POWER_SERVICE);
              handleScreenStateChanged(powerManager.isScreenOn());
          } else {
              // Set the right suspend mode settings
              mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
                      && mUserWantsSuspendOpt.get());

              // Inform WifiConnectivtyManager the screen state in case
              // WifiConnectivityManager missed the last screen update because
              // it was not started yet.
              mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
          }
          mWifiNative.setPowerSave(true);

          if (mP2pSupported) {
              if (mOperationalMode == CONNECT_MODE) {
                  mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
              } else {
                  // P2P statemachine starts in disabled state, and is not enabled until
                  // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
                  // keep it disabled.
              }
          }

          final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
          intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
          intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
          mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);

          // Enable link layer stats gathering
          mWifiNative.setWifiLinkLayerStats("wlan0", 1);
      }
這裡由於藍芽和wifi模組有衝突,將通過 setBluetoothCoexistenceScanMode 函式,是wlan晶片做出調整。
最後切換到 DisconnectedState 狀態。connectModeState 和 DisconnectedState 的enter函式將被執行。
class ConnectModeState extends State {

     @Override
     public void enter() {
         if (DBG) loge(getName() + "\n");

         // Inform WifiConnectivityManager that Wifi is enabled
         if (mWifiConnectivityManager != null) {
             mWifiConnectivityManager.setWifiEnabled(true);
         }
         // Inform metrics that Wifi is Enabled (but not yet connected)
         mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
     }


class DisconnectedState extends State {

  @Override
  public void enter() {
      if (DBG) loge(getName() + "\n");
      // We dont scan frequently if this is a temporary disconnect
      // due to p2p
      if (mTemporarilyDisconnectWifi) {
          mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
          return;
      }
      if (isTemporarilyDontReconnectWifi()) {
          return;
      }

      if (DBG) {
          logd(" Enter DisconnectedState screenOn=" + mScreenOn);
      }

      /** clear the roaming state, if we were roaming, we failed */
      mAutoRoaming = false;

      if (mWifiConnectivityManager != null) {
          mWifiConnectivityManager.handleConnectionStateChanged(
                  WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
      }

      /**
       * If we have no networks saved, the supplicant stops doing the periodic scan.
       * The scans are useful to notify the user of the presence of an open network.
       * Note that these are not wake up scans.
       */
      if (mNoNetworksPeriodicScan != 0 && !mP2pConnected.get()
              && mWifiConfigManager.getSavedNetworks().size() == 0) {
          sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
                  ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
      }

      mDisconnectedTimeStamp = System.currentTimeMillis();
  }
接下來 Disconnected 將處理被延遲的兩條訊息。
case CMD_SET_OPERATIONAL_MODE:
    if (message.arg1 != CONNECT_MODE) {
        mOperationalMode = message.arg1;
        mWifiConfigManager.disableAllNetworksNative();
        if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
            mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
            setWifiState(WIFI_STATE_DISABLED);
        }
        transitionTo(mScanModeState);
    }
    mWifiConfigManager.
            setAndEnableLastSelectedConfiguration(
                    WifiConfiguration.INVALID_NETWORK_ID);
    break;


case CMD_START_DRIVER:
   if (mOperationalMode == CONNECT_MODE) {
       mWifiConfigManager.enableAllNetworks();
   }
   break;
    其中 CMD_SET_OPERATIONAL_MODE 訊息在 Disconnected 中被處理,
    進行設定上一次的網路配置CMD_START_DRIVER 訊息則是在 Disconnected 的 父狀態 
    DriverStartedState 中被處理,目的是開啟所有的網路。
    至此,wifi啟動流程到這裡就結束了,接下來 WifiStateMachine 就停留在這個狀態等待下一個訊息了。