Android wifi源碼分析(一) Wifi啟動流程

最近在解決wifi的一些問題,故研究下wifi源碼。
該源碼是基于Android4.3,其他版本略有改動,大致流程一樣。
這篇主要說一下wifi的啟動流程。


WifiManager

先從wifi的開啟來看,WifiManager中提供了接口用來控制wifi開關,setWifiEnabled,參數true表示開啟、false表示關閉。

public boolean setWifiEnabled(boolean enabled) {
    try {
        return mService.setWifiEnabled(enabled);
    } catch (RemoteException e) {
        return false;
    }
}

該方法調用到WifiService中的setWifiEnabled。


WifiService

public synchronized boolean setWifiEnabled(boolean enable) {
    enforceChangePermission();
    Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid());
    if (DBG) {
        Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
    }

    /*
    * 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;
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }

    mWifiController.sendMessage(CMD_WIFI_TOGGLED);
    return true;
}

enforceChangePermission 判斷調用的進程是否有權限。想要開關wifi需要CHANGE_WIFI_STATE 權限。
handleWifiToggled 判斷飛行模式、保存wifi 操作的狀態。
向WifiController發送消息。CMD_WIFI_TOGGLED


WifiController

WifiController在WIfiService的構造函數中初始化、并開始運行。
WifiController繼承了StateMachine,是一個狀態機。其構造函數如下

WifiController(Context context, WifiService service, Looper looper) {
  super(TAG, looper);
//....
  addState(mDefaultState);
      addState(mApStaDisabledState, mDefaultState);
      addState(mStaEnabledState, mDefaultState);
          addState(mDeviceActiveState, mStaEnabledState);
          addState(mDeviceInactiveState, mStaEnabledState);
              addState(mScanOnlyLockHeldState, mDeviceInactiveState);
              addState(mFullLockHeldState, mDeviceInactiveState);
              addState(mFullHighPerfLockHeldState, mDeviceInactiveState);
              addState(mNoLockHeldState, mDeviceInactiveState);
      addState(mStaDisabledWithScanState, mDefaultState);
      addState(mApEnabledState, mDefaultState);
      addState(mEcmState, mDefaultState);
  if (mSettingsStore.isScanAlwaysAvailable()) {
      setInitialState(mApStaDisabledState);
  } else {
      setInitialState(mApStaDisabledState);
  }
}

上述函數中addState的格式,可以看出各狀態之間的關系。
然后通過wifi是否可以一直掃描(isScanAlwaysAvailable)設置狀態機初始狀態。
ApStaDisabledState和StaDisabledWithScanState兩種狀態處理CMD_WIFI_TOGGLED消息時一樣的。
看看ApStaDisabledState是如何處理的。

class ApStaDisabledState extends State {
    private int mDeferredEnableSerialNumber = 0;
    private boolean mHaveDeferredEnable = false;
    private long mDisabledTimestamp;

    @Override
    public void enter() {
        mWifiStateMachine.setSupplicantRunning(false);
        // Supplicant can't restart right away, so not the time we switched off
        mDisabledTimestamp = SystemClock.elapsedRealtime();
        mDeferredEnableSerialNumber++;
        mHaveDeferredEnable = false;
    }
    @Override
    public boolean processMessage(Message msg) {
        switch (msg.what) {
            case CMD_WIFI_TOGGLED:
            case CMD_AIRPLANE_TOGGLED:
                if (mSettingsStore.isWifiToggleEnabled()) {
                    if (doDeferEnable(msg)) {
                        if (mHaveDeferredEnable) {
                            //  have 2 toggles now, inc serial number an ignore both
                            mDeferredEnableSerialNumber++;
                        }
                        mHaveDeferredEnable = !mHaveDeferredEnable;
                        break;
                    }
                    if (mDeviceIdle == false) {
                        transitionTo(mDeviceActiveState);
                    } else {
                        checkLocksAndTransitionWhenDeviceIdle();
                    }
                }
                break;
            //。。。。。
        }
        return HANDLED;
    }

    private boolean doDeferEnable(Message msg) {
        long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
        if (delaySoFar >= mReEnableDelayMillis) {
            return false;
        }

        log("WifiController msg " + msg + " deferred for " +
                (mReEnableDelayMillis - delaySoFar) + "ms");

        // need to defer this action.
        Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
        deferredMsg.obj = Message.obtain(msg);
        deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
        sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
        return true;
    }
}

mSettingsStore.isWifiToggleEnabled()用來獲取保存的wifi操作的狀態,如果是開則繼續。
wpa supplicant 關閉后不能立即啟動,mReEnableDelayMillis為重新開啟wifi的延時時間(從系統數據庫獲取,獲取不到則默認值為500ms),這里deferEnable用來判斷時間,延緩啟動。
之后切換到DeviceActiveState。
DeviceActiveState為StaEnabledState的子狀態,所以會先調用父狀態的enter()函數,然后調用子狀態的enter()函數。分別看一下兩個enter函數。

1 WifiController --StaEnabledState

先看看StaEnabledState的enter()函數:

class StaEnabledState extends State {  
    @Override  
    public void enter() {  
        mWifiStateMachine.setSupplicantRunning(true);  
    }  
    ...  
    }  
}  

mWifiStateMachine.setSupplicantRunning(true),WifiStateMachine發送Message--CMD_STAET_SUPPLICANT。

WIfiStateMachine

看看處理該消息

class InitialState extends State {
    @Override
    public boolean processMessage(Message message) {
        switch (message.what) {
            case CMD_START_SUPPLICANT:
                //加載Wifi驅動
                if (mWifiNative.loadDriver()) {
                    try {//加載固件
                        mNwService.wifiFirmwareReload(mInterfaceName, "STA");
                    } catch (Exception e) {
                    }

                    try {
                        // A runtime crash can leave the interface up and
                        // this affects connectivity when supplicant starts up.
                        // Ensure interface is down before a supplicant start.
                        mNwService.setInterfaceDown(mInterfaceName);
                        // Set privacy extensions
                        mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);

                       // IPv6 is enabled only as long as access point is connected since:
                       // - IPv6 addresses and routes stick around after disconnection
                       // - kernel is unaware when connected and fails to start IPv6 negotiation
                       // - kernel can start autoconfiguration when 802.1x is not complete
                        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.
                    */
                    mWifiNative.killSupplicant(mP2pSupported);
                    //開啟wpa_supplicant
                    if(mWifiNative.startSupplicant(mP2pSupported)) {
                        setWifiState(WIFI_STATE_ENABLING);
                        if (DBG) log("Supplicant start successful");
                        mWifiMonitor.startMonitoring();
                        transitionTo(mSupplicantStartingState);
                    } else {
                        loge("Failed to start supplicant!");
                    }
                } else {
                    loge("Failed to load driver");
                }
                break;
//.......
}
  1. WifiNative.loadDriver():加載Wifi驅動。
    WifiNative.loadDriver()-->android_net_wifi_wifi.cpp(android_net_wifi_loadDriver)
    ->wifi.c(wifi_load_driver)。

  2. mNwService.wifiFirmwareReload(mInterfaceName, "STA"); 加載固件

  3. mWifiNative.startSupplicant(mP2pSupported) 先保證沒有運行的wpa supplicant,然后開啟wpa supplicant,其中參數mP2pSupported表示是否支持wifi 直連。
    WifiNative.startSupplicant()-->android_net_wifi_wifi.cpp(android_net_wifi_startSupplicant)
    ->wifi.c(wifi_start_supplicant)。

  4. setWifiState(WIFI_STATE_ENABLING) 發送廣播(wifi狀態改變 ,正在開啟)。

  5. mWifiMonitor.startMonitoring()
    WifiMonitor創建一個線程MonitorThrad

  • connectToSupplicant最終調到wifi.c 中的wifi_connect_on_socket_path。在該函數中,將通過wpa_ctrl_open函數分別創建兩個socket,一個是ctrl_conn, 用于向wpa_supplicant發送命令并接收response, 另一個是monitor_conn, 它一直阻塞等待從wpa_supplicant過來的event。連接成功后WifiMonitor會向WifiStateMachine發送一個代表socket通信建立成功的消息:SUP_CONNECTION_EVENT。
  • mWifiNative.waitForEvent-》wifi_wait_on_socket ,循環調用該函數,接收底層事件并分發處理。
  1. WifiStateMachine切換到SupplicantStartingState狀態。步驟5是在另一個線程中運行,并且是耗時操作,所以WifiStateMachine先切換到SupplicantStartingState狀態,然后接收到SUP_CONNECTION_EVENT消息。

下面看看如何處理SUP_CONNECTION_EVENT消息。

class SupplicantStartingState extends State {
    @Override
    public boolean processMessage(Message message) {
        switch(message.what) {
            case WifiMonitor.SUP_CONNECTION_EVENT:
                if (DBG) log("Supplicant connection established");
                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());
                mWifiConfigStore.loadAndEnableAllNetworks();
                initializeWpsDetails();

                sendSupplicantConnectionChangedBroadcast(true);
                transitionTo(mDriverStartedState);
                break;
//......
}

setWifiState(WIFI_STATE_ENABLED),發送WIFI_STATE_ENABLED的廣播。
WifiConfigStore.loadAndEnableAllNetworks() 加載并enable所有保存在wpa_supplicant中的AP。這樣會使這些保存的wifi實現自動重連。
切換到DriverStartedState狀態。
DriverStartedState是SupplicantStartedState的子狀態,所以先后運行SupplicantStartedState、DriverStartedState的enter方法。

class SupplicantStartedState extends State {
        @Override
        public void enter() {
            /* Wifi is available as long as we have a connection to supplicant */
            mNetworkInfo.setIsAvailable(true);

            int defaultInterval = mContext.getResources().getInteger(
                    R.integer.config_wifi_supplicant_scan_interval);
            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
                    defaultInterval);
            //設置掃描時間間隔。
            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
        }
//.....

frameworks/base/core/res/res/values/config.xml中可以修改config_wifi_supplicant_scan_interval 的值。
mNetworkInfo.setIsAvailable(true); 連接到wpa_supplicant,就可以使用wifi。
setScanInterval 設置wifi掃描間隔時間。

class DriverStartedState extends State {
        @Override
        public void enter() {
            mIsRunning = true;
            mInDelayedStop = false;
            mDelayedStopCounter++;
            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);
            /* set country code */
            setCountryCode();
            /* set frequency band of operation */
            setFrequencyBand();
            /* initialize network state */
            setNetworkDetailedState(DetailedState.DISCONNECTED);

            /* Remove any filtering on Multicast v6 at start */
            mWifiNative.stopFilteringMulticastV6Packets();

            /* Reset Multicast v4 filtering state */
            if (mFilteringMulticastV4Packets.get()) {
                mWifiNative.startFilteringMulticastV4Packets();
            } else {
                mWifiNative.stopFilteringMulticastV4Packets();
            }

            if (mOperationalMode != CONNECT_MODE) {
                mWifiNative.disconnect();
                transitionTo(mScanModeState);
            } else {
                /* Driver stop may have disabled networks, enable right after start */
                mWifiConfigStore.enableAllNetworks();
                mWifiNative.reconnect();
                // 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
                mWifiNative.status();
                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());
            }
            mWifiNative.setPowerSave(true);

            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);

            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);
        }
//.....

setBluetoothCoexistenceScanMode 啟用藍牙連接時啟用藍牙共存掃描模式。當此模式打開時,驅動程序使用的一些低級掃描參數會被更改,以減少對藍牙的干擾。
mOperationalMode 默認為CONNECT_MODE,enableAllNetworks 將保存的wifi置為可連接的,并進行重連。
mWifiNative.status() 將wpa_supplicant中狀態進行同步。
切換到DisconnectedState狀態。

2 WifiController --DeviceActiveState

/* Parent: StaEnabledState */  
class DeviceActiveState extends State {  
    @Override  
    public void enter() {  
        mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);  
        mWifiStateMachine.setDriverStart(true);  
        mWifiStateMachine.setHighPerfModeEnabled(false);
    }
    ...  
} 

STA的3個操作狀態:CONNECT_MODE,SCAN_ONLY_MODE,SCAN_ONLY_WIFI_OFF_MODE
*在CONNECT_MODE中,STA可以掃描并連接到接入點
*在SCAN_ONLY_MODE中,STA只能掃描接入點
*在SCAN_ONLY_WIFI_OFF_MODE中,STA只能掃描(wifi關閉狀態下)
setOperationalMode 這里設置模式 可掃描、可連接。

setDriverStart(true) 發送CMD_START_DRIVER 消息。該消息在DriverStartedState中處理。

case CMD_START_DRIVER:
    if (mInDelayedStop) {
        mInDelayedStop = false;
        mDelayedStopCounter++;
        mAlarmManager.cancel(mDriverStopIntent);
        if (DBG) log("Delayed stop ignored due to start");
        if (mOperationalMode == CONNECT_MODE) {
            mWifiConfigStore.enableAllNetworks();
        }
    }
    break;

如果正在延時關閉驅動,則取消關閉。并將保存的wifi都Enable。

setHighPerfModeEnabled暫時不清楚什么情況。

這就差不多整理完wifi啟動的流程了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容