android4.1 Wifi 淺析

簡單分析下wifi相關類,首先了解幾個主要概念html

AsyncChanneljava

簡單理解:android

AsyncChannel,就是藉助Messenger 機制,讓兩個不一樣的handler之間進行通訊。shell

AsyncChannel類有兩個Messenger對象:mSrcMessenger,mDstMessenger。安全

mSrcMessenger,通常用於封裝源端handler對象服務器

mDstMessenger,通常用於封裝目的端handler對象網絡

然後,調用AsyncChannel的sendMessage方法時,執行兩個操做:socket

1. msg.replyTo = mSrcMessenger; //重設消息的replyTo值函數

2. mDstMessenger.send(msg); //該方法就會將消息發送給目的端的handler去處理測試

因爲重設了消息的replyTo值,以後在目的端發出的消息,將返回給源端handler處理。

 

參考:Android源碼閱讀筆記:AsyncChannel與層次狀態機StateMachine

 

wifi 相關文件

framework
WifiManager
    管理全部wifi操做,提供API調用,主要用於上層應用調用
WifiService
    處理Wifi操做請求。通常都是在WifiManager中被調用
WifiStateMachine
    處理wifi各個階段狀態所要處理的消息
WifiMonitor
    監聽全部來自wpa_supplicant的事件,並傳遞給WifiStateMachine處理
WifiNative
    native方法,發送消息給wpa_supplicant

jni層
android_net_wifi_Wifi.cpp

wpa_supplicant適配器層
wifi.c

wpa_supplicant層
wpa_supplicant.c

 

 

接下來分析wifi啓動、掃描、鏈接ap的流程。

1、 Wifi相關類建立

1.SystemServer::ServerThread::run---->new WifiService(...);

2.WifiService(...)---->new WifiStateMachine(...);

3.WifiStateMachine(...)

---->new WifiNative(...)

---->new WifiMonitor(...)

---->AsyncChannel::connectSync(...)

 

ps: AsyncChannel::connectSync將WifiStateMachine和wifiApConfigStore經過AsyncChannel關聯起來。

 
4. SystemServer::ServerThread::run ServiceManager.addService(...);

 

 over~~

-----------------------------------------------------------------

 

2、 wifi功能啓動

先貼出總體流程時序圖

1. 檢查系統屬性中的wifi狀態信息

SystemServer::ServerThread::run
--->WifiService::checkAndStartWifi (該方法只在開機時調用,用來檢查wifi是否須要開啓)

分析WifiService::checkAndStartWifi方法:

    public void checkAndStartWifi() {
        mAirplaneModeOn.set(isAirplaneModeOn());
        mPersistWifiState.set(getPersistedWifiState());
        /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */
        boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
        Slog.i(TAG, "WifiService starting up with Wi-Fi " +
                (wifiEnabled ? "enabled" : "disabled"));

        // If we are already disabled (could be due to airplane mode), avoid changing persist
        // state here
        if (wifiEnabled) setWifiEnabled(wifiEnabled);

        mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
               makeWifiWatchdogStateMachine(mContext); //之後分析

    }

(1)isAirplaneModeOn()/getPersistedWifiState():都是獲取系統屬性進行判斷。

(2)shouldWifiBeEnabled():判斷mAirplaneModeOn/mPersistWifiState中的值,其實就是上述兩個方法得到的值。

(3)testAndClearWifiSavedState():獲取系統屬性中保存的wifi狀態的值,返回,以後清空狀態,置爲0。

另注意:如下分析都是基於條件:開機啓動時,wifi狀態爲on。

 

所以,接着會調用setWifiEnabled(true)方法,下面分析。

到此,檢查wifi系統屬性狀態的操做結束,over~~

 

2.setWifiEnabled(true)執行流程

2.1

該方法中主要執行如下語句:

        if (FeatureQuery.FEATURE_CT_FMC_SUPPORT && WifiManager.fmcV2Support()) {
            mWifiStateMachine.fmcV2SetWifiEnabled(enable);
        } else {
            mWifiStateMachine.setWifiEnabled(enable);
        }

分析:

FeatureQuery.FEATURE_CT_FMC_SUPPORT/WifiManager.fmcV2Support()

(1)FeatureQuery.FEATURE_CT_FMC_SUPPORT 

這個屬性值在build/buildplus/target/FeatureQuery.java裏能夠找到。

 

(2)WifiManager::fmcV2Support方法以下

    public static boolean fmcV2Support() {
        return (SystemProperties.getInt("ro.config.cwenable", 0) == 1)
                 && (SystemProperties.getInt("ro.config.fmcv2support", 0) == 1);
    }

 

就是獲取兩個系統屬性,判斷是否支持FMC功能,有兩種方式能夠查看

(a)out/target/product/msm8625/system/build.prop中能夠找到

(b)命令提示符下,adb shell,而後使用命令:getprop XX ,就能夠得到屬性值。XX表示系統屬性名。

總結:這兩個條件的做用就是判斷是否支持FMC功能(固定網絡與移動網絡融合)

 

2.2

行mWifiStateMachine.fmcV2SetWifiEnabled法:

    public void fmcV2SetWifiEnabled(boolean enable) {
        if (!enable) {
            ......
        } else {
            setWifiEnabled(enable);
        }
    }

 

接着調用WifiStateMachine::setWifiEnabled(enable): 發送兩個消息。

    public void setWifiEnabled(boolean enable) {
        mLastEnableUid.set(Binder.getCallingUid());
        if (enable) {
            /* Argument is the state that is entered prior to load */
            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
            sendMessage(CMD_START_SUPPLICANT);
        } else {
           ......
        }
    }

接收CMD_LOAD_DRIVER消息的是DriverUnloadedState狀態。

 

2.4 DriverUnloadedState狀態處理

public boolean processMessage(Message message) {
      if (DBG) log(getName() + message.toString() + "\n");
      switch (message.what) {
           case CMD_LOAD_DRIVER:
               transitionTo(mDriverLoadingState);
               break;
               ......
}

在這裏CMD_LOAD_DRIVER消息被處理,切換到DriverLoadingState狀態。

 

2.5 DriverLoadingState 處理狀態

public void enter() {
  ......
  final Message message = new Message();
  message.copyFrom(getCurrentMessage());
  new Thread(new Runnable() {
                public void run() {
                    mWakeLock.acquire();
                    //enabling state
                    switch(message.arg1) {
                        case WIFI_STATE_ENABLING:
                            setWifiState(WIFI_STATE_ENABLING);
                            break;
                        case WIFI_AP_STATE_ENABLING:
                            setWifiApState(WIFI_AP_STATE_ENABLING);
                            break;
                    }

                    if(mWifiNative.loadDriver()) {
                        if (DBG) log("Driver load successful");
                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
                  }
        .....
}

(1)enter方法中經過message.copyFrom(getCurrentMessage())從新得到了(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0)息,而後開啓一個線程來處理。

(2)線程中首先判斷消息參數arg1,進入」case WIFI_STATE_ENABLING調用setWifiState方法。該方法主要是用來設置電池使用統計中的wifi信息。而後會發出一個WIFI_STATE_CHANGED_ACTION廣播。

(3)調用 WifiNative.loadDriver()方法加載wifi相關驅動,成功後發送CMD_LOAD_DRIVER_SUCCESS消息。該消息會在當前DriverLoadingState狀態的processMessage方法中被處理。

(4)處理CMD_LOAD_DRIVER_SUCCESS消息

case CMD_LOAD_DRIVER_SUCCESS:
      transitionTo(mDriverLoadedState);
      break;
.....

狀態切換到DriverLoadedState

 

2.6 DriverLoadedState狀態處理

處理WifiStateMachine::setWifiEnabled方法發出的CMD_START_SUPPLICANT消息(在此以前,該消息一直未被處理)

case CMD_START_SUPPLICANT:
   ......
     if(mWifiNative.startSupplicant(mP2pSupported)) {
            if (DBG) log("Supplicant start successful");
            mWifiMonitor.startMonitoring();
            transitionTo(mSupplicantStartingState);
     } 
     ......
}

(1)啓動wpa_supplicant:

調用startSupplicant方法啓動。

(2)啓動WifiMonitor監聽

startMonitoring

--->new MonitorThread().start()

--->MonitorThread::run

--->WifiMonitor::connectToSupplicant()

--->WifiNative::connectToSupplicant() [最多嘗試5次]

       ---> mStateMachine.sendMessage(SUP_CONNECTION_EVENT)

最後成功鏈接上wpa_supplicant後,發出SUP_CONNECTION_EVENT消息。

 

(3)切換到SupplicantStartingState狀態

處理SUP_CONNECTION_EVENT消息:

case WifiMonitor.SUP_CONNECTION_EVENT:
      if (DBG) log("Supplicant connection established");
      setWifiState(WIFI_STATE_ENABLED);
    ......
      sendSupplicantConnectionChangedBroadcast(true);
      transitionTo(mDriverStartedState);
      break;
......

設置電池統計爲enabled,發出WIFI_STATE_CHANGED_ACTION廣播。這個廣播會被wifi應用和狀態欄接收處理;sendSupplicantConnectionChangedBroadcast方法發出廣播,只在測試代碼中被處理;最後切換到DriverStartedState狀態。

 

到此爲止,開機啓動wifi的工做就完成了,over~~

-----------------------------------------------------------------

3、打開wifi,自動掃描AP

1. WifiSettings類構造函數:

(1)定義了一個廣播;

(2)建立了一個Scanner對象(Scanner類是一個Handler子類,用來啓動掃描功能)

2. WifiSettings::onActivityCreated

---->new WifiEnabler(...)

---->WifiEnabler::resume()

---->resume方法中,爲Switch開關設置了checkedChange的監聽方法。

 

3. 初始化相關類對象的工做已完成,如今打開wifi

首先點擊Switch開關,將觸發onCheckedChanged方法。在這裏,會先對飛行模式、wifi的狀態進行判斷。

---->WifiManger::setWifiEnabled

---->WifiService::setWifiEnabled

---->WifiStateMachine::setWifiEnabled

---->接下來的操做同」 開機wifi功能啓動」。直到開機wifi功能啓動完畢。

此時,WifiStateMachine::SupplicantStartingState::processMessage:

---->case WifiMonitor.SUP_CONNECTION_EVENT:

setWifiState(WIFI_STATE_ENABLED); //wifi狀態爲enabled

......

該方法會發送一個WIFI_STATE_CHANGED_ACTION的粘性廣播。這個廣播會被WifiSettings類接收到。

 

4. WifiSettings處理WIFI_STATE_CHANGED_ACTION廣播

BroadcastReceiver::onReceive

---->handleEvent

---->updateWifiState  //在該方法中,接着會調用Scanner::resume方法。

---->Scanner::resume

---->Scanner::sendEmptyMessage(0) 

---->Scanner::handleMessage

調用WifiManager::startScanActive方法進行掃描

---->WifiManager::startScanActive

---->WifiService::startScan

---->WifiStateMachine::startScan //發出CMD_START_SCAN消息

該消息會被DriverstartedState狀態處理。

---->WifiNative::scan

接着就調用到native層去了...這裏就不分析下去了,等後面總體分析...

分析完畢,over~~

-----------------------------------------------------------------

 

4、打開wifi,鏈接AP

分紅兩個部分分析:

1.wifi ap 提示框的建立

2.輸入密碼,鏈接ap

 

1. wifi ap 提示框的建立

初始條件:這裏分析安全性爲加密的ap

1.1 點擊某個ap,調用流程:

---->WifiSettings::onPreferenceTreeClick

---->WifiSettings::showDialog(選擇的AP, false)

接着調用父類的showDialog方法

---->SettingsPreferenceFragment::showDialog(WIFI_DIALOG_ID) 

SettingsPreferenceFragment類中,會建立一個SettingsDialogFragment對象,代碼以下:

    protected void showDialog(int dialogId) {
        if (mDialogFragment != null) {
            Log.e(TAG, "Old dialog fragment not null!");
        }
        //建立一個SettingsDialogFragment對象
        mDialogFragment = new SettingsDialogFragment(this, dialogId); //(1)
        mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); //(2)
    }

(1) SettingsDialogFragment類繼承自DialogFragment類,會在onCreate後調用onCreateDialog方法。

---->SettingsDialogFragment(DialogCreatable fragment, int dialogId)

---->SettingsDialogFragment::onCreateDialog()

由於初始狀況下,savedInstanceStatenull,因此該方法直接回調構造函數中傳入的fragment參數的onCreateDialog方法,即WifiSettings::onCreateDialog

---->WifiSettings::onCreateDialog

 

回到WifiSettings類:

WifiSettings::onCreateDialog方法中,會建立一個WifiDialog類對象。

跳轉到WifiDialog類:

執行WifiDialog類的onCreate方法,會發現它建立了一個WifiConfigController類對象。

再看WifiConfigController類:

該類的構造函數主要是初始化了ap提示框的界面,固然還包括」添加網絡」的界面。

這樣,界面就建立了,須要顯示出來,這就是下一步的做用了。

 

(2)調用了DialogFragment::show方法,將提示框顯示出來。

至此,提示框分析完畢。

 

2. 輸入密碼,鏈接ap

(1)點擊ap提示框的「鏈接」按鈕,會調用WifiSettings::onClick方法:

    public void onClick(DialogInterface dialogInterface, int button) {
        if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
            forget();
        } else if (button == WifiDialog.BUTTON_SUBMIT) {
            if (FeatureQuery.FEATURE_CT_FMC_SUPPORT) {
            shouldShowErrorMsg = true;
            }
            submit(mDialog.getController());
        }
    }

(2) 接着便調用submit方法,參數爲WifiConfigController類對象。

(3) submit方法中會調用WifiManager::connect方法進行鏈接。

over~~

-----------------------------------------------------------------

 

分析framework-->native-->wpa_supplicant幾個方法的調用流程

mWifiNative.isDriverLoaded()

---> android_net_wifi_Wifi.cpp::android_net_wifi_isDriverLoaded

---> wifi.c:: is_wifi_driver_loaded()
--->查看系統屬性wlan.driver.status(ok爲已加載驅動,unloaded爲未加載)

 

mWifiNative.loadDriver()

---> android_net_wifi_Wifi.cpp::android_net_wifi_loadDriver

---> wifi.c:: wifi_load_driver()
   ---> ensure_wlan_driver_config_file_exists 
      //首先判斷wifi的驅動配置文件是否加載
     --->property_set  //設置屬性
     --->insmod //install modules 載入驅動模塊等

 

mWifiNative.startSupplicant(mP2pSupported)
參數爲mP2pSupported,便是否支持p2p

---> android_net_wifi_Wifi.cpp::android_net_wifi_startSupplicant

---> wifi.c::wifi_start_supplicant(p2pSupported)
   ---> ensure_config_file_exists
      //首先判斷wifi的配置文件是否加載
"/data/misc/wifi/wpa_supplicant.conf";
"/data/misc/wifi/p2p_supplicant.conf";
     --->property_set("ctl.start", supplicant_name);  
 // 這裏supplicant_name爲p2p_supplicant,因此就會啓動init.rc中名爲p2p_supplicant的服務了

 

ps:

property_set("ctl.start",xxx)

property_set("ctl.stop",xxx) 

來使得android property service 去開啓或結束 xxx service.

 

由於init.qcom.rcservice p2p_supplicant中已經包含了wpa_supplicant啓動所需的語句及參數,因此後面會開始執行wpa_supplicant/main.c::main方法來初始化wpa_supplicant了。

wpa_supplicant初始化  見wpa_supplicant初始化

 

WifiMonitor::connectToSupplicant

---> mWifiNative.connectToSupplicant()
---> mWifiNative.connectToSupplicant(mInterface)
    //mInterface是建立WifiNative對象的時候傳遞進來的參數

---> android_net_wifi_Wifi::android_net_wifi_connectToSupplicant

---> wifi.c:: wifi_connect_to_supplicant(ifname.c_str())

---> 首先判斷文件IFACE_DIR = "/data/system/wpa_supplicant";是否存在,
     存在:ifname="/data/system/wpa_supplicant/wlan0";
   不存在:ifname=wlan0

---> wifi_connect_on_socket_path
    /* 首先,判斷系統屬性:
   * wlan.driver.status 是否爲ok
   * init.svc.p2p_supplicant 是否爲running
   * 而後,建立兩個socket(經過wpa_ctrl_open方法)
   * ctrl_conn:用於向wpa_supplicant發送命令並接收response
   * monitor_conn:它一直阻塞等待從wpa_supplicant過來的event
   * 最後調用wpa_ctrl_attach將monitor_conn信息註冊到wpa_supplicant
   */

 

wpa_ctrl_attach做用:因爲socket是數據報方式的,這一步是必須的,對於存在於wpa_supplicant的服務器端,它是全部客戶端共享的,因爲它須要主動向monitor_conn客戶端發送事件,因此它必須先記錄下該客戶端的詳細信息,wpa_supplicant就能夠將event發向該socket。

這樣,就經過socket,創建起native-->wpa_supplicant的鏈接了。

 

mWifiNative.waitForEvent()
該函數是有返回值的,返回值即爲wpa_supplicant傳遞過來的消息。

java層會經過jni方式調用android_net_wifi_waitForEvent(在線程MonitorThread中調用)方法,該方法會調用wifi_wait_for_event。在wifi_wait_for_event方法裏,會阻塞接收從wpa_supplicant模塊傳來的事件,一旦wpa_supplicant模塊發出事件,wifi_wait_for_event接收到後,會將包含事件的buf經過函數參數的方式回傳到java層,java收到事件後,再繼續調用wifi_wait_for_event函數進行阻塞等待接收,從而完成一個循環。

---> mWifiNative::waitForEvent(mInterface)

---> android_net_wifi_Wifi.cpp::android_net_wifi_waitForEvent

---> wifi.c::wifi_wait_for_event(ifname.c_str(), buf, sizeof buf)
  ---> wifi_wait_on_socket
  ---> wifi_ctrl_recv
  ---> wpa_ctrl_recv
  ---> recv
ps:從wpa_supplicant傳遞過來的值由recv函數接收,一直向上傳遞到buf參數中,最終返回return env->NewStringUTF(buf)給WifiMonitor。
相關文章
相關標籤/搜索