Android WIFI 詳解

最 近研究Wifi模塊,查了很多的相關資料,但發現基本上是基於android2.0版本的的分析,而如今研發的android移動平臺基本上都是2.3的 版本,跟2.0版本的差異,在Wifi模塊上也是顯而易見的。2.3版本Wifi模塊沒有了WifiLayer,以前的WifiLayer主要負責一些復 雜的Wifi功能,如AP選擇等以提供給用戶自定義,而新的版本里面的這塊內容基本上被WifiSettings所代替。 java

       本文就是基於android2.3版本的Wifi分析,主要分爲兩部分來分別說明: android

(1)    Wifi模塊相關文件的解析 編程

(2)    Wpa_supplicant解析 數組

(3)    Wifi的啓動流程(有代碼供參考分析) 服務器

一,Wifi模塊相關文件解析 cookie

1)     wifisettings.java 網絡

packages/apps/Settings/src/com/android/settings/wifiwifisettings.java 數據結構

       該類數據部分主要定義了下面幾個類的變量: app

{ 框架

private final IntentFilter mFilter;

 

//廣播接收器,用來接收消息並作響應的處理工做

privatefinal BroadcastReceiver mReceiver;

 

//這是一個掃描類,會在用戶手動掃描   AP時被調用

privatefinal Scanner mScanner;               

private WifiInfo mLastInfo;

 

       //服務代理端,做爲WifiService對外的接口類呈現

privateWifiManager mWifiManager;

 

//這個類主要實現Wifi的開閉工做

privateWifiEnabler mWifiEnabler;

 

//AP

private AccessPoint mSelected;

private WifiDialog mDialog;

……

}

       wifiSettings類的構造函數的主要工做:定義了一個IntentFilter(Intent過濾器)變量,並添加了六個動做,(瞭解 Android的intent機制的同窗都知道什麼意思,不明白的同窗參考Intent機制的資料)接着定義一個廣播接收器,並有相應的消息處理函數,下 面是該構造函數的定義:

 

public WifiSettings() {

mFilter = new IntentFilter();

//intent機制中的intent消息過濾器,下面添加能夠處理的動做

    mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);

      mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);

    mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);

   mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);

       mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

    mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);

 

//註冊了廣播接收器,用來處理接收到的消息事件

       mReceiver = new BroadcastReceiver() {

           @Override

            public void onReceive(Context context,Intent intent) {

               handleEvent(intent); //事件處理函數

            }

        };

 

        mScanner= new Scanner();     //手動掃描類

}

       在廣播接收器中的相應函數onReceive函數中有個handleEvent函數,它就是用來處理廣播接收器接受到的intent消息的,它的功能是根 據intent消息中的動做類型,來執行相應的操做,每一種動做對應了activity的一項消息處理能力。

       在oncreate函數中實例化了mWifiManager和mWifiEnabler兩個類,這兩個類對wifiSettings來講相當重要,它後面的定義的一系列函數都是經過調用這兩個類的相應接口來實現的。

……

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

mWifiEnabler = new WifiEnabler(this,

                   (CheckBoxPreference) findPreference("enable_wifi"));

……

       WifiSettings中還定義了顯示菜單和響應菜單鍵的函數,即onCreateOptionsMenu()和 onOptionsItemSelected();還有響應配置對話框中按鍵的onClick()函數;最後定義了Scanner類,它是一個 handler的繼承類,實現了消息處理函數,用於處理手動掃描的動做。

 

2)     WifiEnabler.java:

packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java

 

private final Context mContext;

private final CheckBoxPreference mCheckBox;

 

//兩個重要成員

private final WifiManager mWifiManager;

private final IntentFilter mIntentFilter;

 

       wifienabler類中定義了四個成員變量很重要,mContext,mCheckBox,mWifiManager和mReceiver,其中 mContext用於獲取mwifiManager實例,mReceiver用來接收底層發來的消息,mCheckBox用來改變UI的狀態。

該 類中定義了幾個重要的函數onPreferenceChange,handleWifiStateChanged和 handleStateChanged,onPreferenceChange用來處理按下的Enbler鍵,它會調用 mWifiManager.setWifiEnabled(enable),另外兩個用來處理接受的消息事件。

 

       在類的構造函數中,主要作了一下工做:初始化了mContext,mCheckBox,mWifimanager,而且初始化了一個 mIntentFilter變量,添加了三個動做,在構造函數的上面定義了一個廣播接收器,用來接收下層傳來的消息,並根據intent動做的類型調用相 應的處理函數,這個廣播接收器在onResum函數中被註冊。

public WifiEnabler(Context context, CheckBoxPreferencecheckBox) {

        mContext= context;

       mCheckBox = checkBox;

       mOriginalSummary = checkBox.getSummary();

       checkBox.setPersistent(false);

 

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

        mIntentFilter= new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);

        // Theorder matters! We really should not depend on this. :(

       mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);

       mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

}

這 裏能夠總結爲:若是上層須要監聽或收到下層的消息,那麼就要經過定義一個BroadcastReciever,並將它註冊,固然在接受到消息後應該有處理 消息的函數,而後在onReciever函數中根據消息調用相應的處理函數,這裏的消息通知機制是Intent,在BroadcastReciever類 的onReciever函數的參數中能夠看出。

該類成員函數的也是經過調用mWifimanager的接口來實現的。

 

 3) WifiManager:

       frameworks/base/wifi/java/android/net/wifi/WifiManager.java

       兩個重要的數據成員:

//WifiService

IWifiManager mService;

    HandlermHandler;

IWifiManager mService和HandlermHandler,這個類擁有了一個WifiService實例,就能夠經過它進行一系列的調 用;WifiManager中定義了的wifi和ap的狀態,這些狀態會在其餘不少類中有使用;而後定義了大量的函數,這些函數幾乎都是對 WifiService接口函數的封裝,直接調用WifiService的函數。

該類的構造函數很簡單:

public WifiManager(IWifiManager service,Handler handler) {

       mService = service;

       mHandler = handler;

    }

該 類中還定義了一個WifiLock類,這個類用來保證在有應用程序使用Wifi無線電傳輸數據時,wifiradio可用,即當一個應用程序使用wifi 的radio進行無線電數據傳輸時,就要先得到這個鎖,若是該鎖已被其餘程序佔有,就要等到該鎖被釋放後才能得到,只用當全部持有該鎖的程序都釋放該鎖 後,才能關閉radio功能。

 

 4)WifiService:

frameworks/base/services/java/com/android/server/WifiService.java

private final WifiStateTrackermWifiStateTracker;

private Context mContext;

private WifiWatchdogServicemWifiWatchdogService = null;

private final  WifiHandler mWifiHandler;

這是WifiService中的幾個重要的數據成員。

在 接下來的構造函數中初始化了mWifiStateTracker,mContext,而後動態生成mWifiThread子線程並啓動,在主線程裏用 mWifiThread調用getLooper()函數得到線程的looper,來初始化建立一個mWifiHandler對象,這個 WifiHandler在WifiService類的後面有定義,並重載了Handler類的handlermessage()函數,這樣消息就能夠在主 線程裏被處理了,這是android的handlerthread消息處理機制,可參考相關資料,這裏不予詳述。在構造函數的最後,註冊了兩個廣播接收 器,分別用來ACTION_AIRPLANE_MODE_CHANGED和ACTION_TETHER_STATE_CHANGED這兩個動做,這裏是 android的intent消息通知機制,請參考相關資料,代碼以下:

 

mContext = context;

mWifiStateTracker = tracker;

mWifiStateTracker.enableRssiPolling(true);

……

HandlerThread wifiThread = newHandlerThread("WifiService");

wifiThread.start();

mWifiHandler = newWifiHandler(wifiThread.getLooper());

……

隨 後定義了一系列的函數,其中有服務器要發送的命令的系列函數,它經過mWifiStateTracker成員類調用本身的的發送命令的接口(其實就是對本 地接口的一個封裝),最後經過適配層發送命令給wpa_supplicant,而事件處理只到WifiStateTracker層被處理。

要 注意的是,在WifiService中,定義了一些函數來建立消息,並經過mWifiHandler將消息發送到消息隊列上,而後在 mHandlerThread線程體run()分發\處理消息,在主線程中被mWifiHandler的handlerMessage()函數處理,最後 調用mWifiStateTracker的對應函數來實現的。這裏我也不明白爲何WifiService不直接調用mWifiStateTracker 對應的函數,還要經過消息處理機制,繞了一圈在調用,固然Google這麼作確定是有它道理的,忘高手指點。

 

 5) WifiStateTracker類

frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

NetworkStateTracker繼承了handler類,而WifiStateTracker繼承了NetworkStateTracker類,就是說WifiStateTracker間接繼承了handler類,屬於一個事件處理類。

WifiStateTracker類首先定義了事件日誌和事件碼(這裏包含了全部可能的事件類型),還定義了以下的重要成員數據:

          //幾個重要的數據成員

private WifiMonitor mWifiMonitor;    //被啓動用來監聽supplicant傳來的消息

private WifiInfo mWifiInfo;        

private WifiManager mWM;             //服務代理

private DhcpHandler mDhcpTarget;  //IP地址獲取線程

private DhcpInfo mDhcpInfo;            //Dhcp的相關信息都在這裏

               

類的構造函數中,初始化了系列成員變量,包括生成了WifiMonitor的實例,在構造函數中,由於WifiStateTracker是一個handler間接子類,因此他會自動調用handler的無參構造函數,得到looper和Queue消息隊列。

而後定義了一些設置supplicant和更新網絡信息的輔助函數。

       startEventLoop()函數很重要,用來啓動WifiMonitor線程,進入消息循環檢測。接着定義了系列通知函數,被 WifiMonitor調用來向WifiStateTracker傳遞從wpa_supplicant接收到的消息,他會調用消息發送函數到消息隊列,並 被WifiStateTracker的handlermessage()函數處理。這個handlermessage()函數就是在隨後被定義的,它主要 是調用相應的輔助函數完成動做,並可能會將消息封裝後,經過intent機制發送出去,被上層的UI活動接收處理。

這 裏也定義了不少的WfiNative接口函數,這是JNI的本地接口;類DhcpHandler extends Handler{}也是在該類中定義的,它也是一個handler的子類,用來處理DHCP相關的消息EVENT_DHCP_START,能夠想到它和 WifiStateTracker不是共用一個looper。

       注意:handleMessage是在該文件中定義的,用來處理經WifiMonitor轉換過的消息。

 

 6) WifiMonitor

       frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

聲明瞭一個重要的成員變量:mWifiStateTracker,並在構造函數中由參數提供初始化,還定義了一系列的可能從wpa_supplicant層接收的事件類型及其名字,這些是消息處理機制的基礎。

startMonitoring()函數,這是一個線程啓動的封裝函數,WifiStateTracker就是經過這個函數啓動的WifiThread。

這 個重要的類classMonitorThreadextends Thread{};它是一個監控進程類,裏面有一系列的事件處理函數和一個重要的Run()函數,run函數主要流 程:connectToSupplicant()鏈接精靈進程wpa_supplicant,這裏有一個 mWifiStateTracker.notifySupplicantXXX()的調用,通知上層是否鏈接成功,而後就是一個輪詢過程,其中調用了 WifiNative.waitForEvent()本地輪詢函數接口,並從返回的事件字符串類型中提取事件的名稱,最後經過事件的名稱調用相應的事件處 理函數,並將事件轉換成mWifiStateTracker能識別的類型上報。

       注意:這裏的每一個事件的捕獲(由wifimonitor完成),最終都會調用相應的mWifiStateTracker的消息通知函數上報消息;輪詢和事件處理都是在Monitor線程類中實現的。

 

 7)WifiNative

       frameworks/base/wifi/java/android/net/wifi/WifiNative.java

裏面定義了一個類WifiNative:其中聲明瞭許多本地接口,可由native的標誌看出,這是Java代碼和本地庫之間的聯繫接口;

 

 8) android_net_wifi_Wifi.java

frameworks/base/core/jni/

       裏面定義了許多的JNI的本地代碼實現,每一個實現中都會調用wpa_supplicant適配層的接口,經過包含適配層的頭文件wifi.h獲取適配層定 義的接口;後面是一個JNINativeMethod數組,定義了每一個本地接口和本地實現的對應關係;最後有一個註冊函數 regester_XXX_XX(),用以把上面的方法類數組註冊到系統中。

       該類實現了本地接口的相關功能,並經過調用wpa的適配層的相關函數和wpa_supplicant通訊,因此JNI是鏈接Java框架層和wpa適配層的橋樑.

 

 9)wpa_supplicant適配層,wifi.c:目錄libhardware/wifi/

       裏面定義不少字符串變量和適配層的接口實現,是對wpa_supplicant程序通訊的接口封裝,用來完成上層和wpa_supplicant的通訊, 頭文件在libhardware/include/hardware下,這裏的函數用來向JNI的本地實現提供調用接口。

       這裏的函數,我把它們分爲三類函數:

一 類是命令相關的(控制)函數,就是在JNI層android_XXX_Command()函數所調用的::Wifi_Command()函數,調用流 程:android_XXX_command()=>docommand()=>wifi_command()=> wifi_send_command()=>wpa_ctrl_require()。

二類是監聽函數,即Wifi_wait_for_event()函數,調用流程:android_net_wifi_Waitforevent()=>wifi_wait_for_event()=>wpa_ctrl_recv()。

三類是剩下的函數。

 

10)wpa_supplicant與上層的接口,wpa_ctrl.c:external/wpa_supplicant

定義了三類套接字,並分別實現了和wpa_supplicant的通訊,所以wpa_supplicant適配層和wpa_supplicant層是經過socket通信的。

要 是從wifi.c中真的很難看出它和wpa_supplicant有什麼關係,和它聯繫密切的是wpa_ctrl.h文件,這裏面定義了一個類 wpa_ctrl,這個類中聲明瞭兩個Socket套接口,一個是本地一個是要鏈接的套接口,wpa_ctrl與wpa_supplicant的通訊就需 要socket來幫忙了,而wpa_supplicant就是經過調用wpa_ctrl.h中定義的函數和wpa_supplicant進行通信 的,wpa_ctrl類(實際上是其中的兩個socket)就是他們之間的橋樑。

 

 11)wpa_supplicant和driver_wext驅動接口的聯繫:

       driver.h:該文件定義了系列結構,首先是一個wpa_scan_result結構,這是一個掃描結果的通用格式,裏面包含了掃描的全部信息(如 BSSID,SSID,信號質量,噪音水平,支持的最大波特率等等信息),每一個驅動接口實現負責將從驅動中上傳的掃描信息的格式轉換到該通用的掃描信息格 式;而後是一些宏定義和枚舉定義,最後也是最重要的是wpa_driver_ops結構,該結構是wpa driver的操做函數集合,裏面有驅動接口的名稱和不少的函數指針。

       drviers.c:該文件很簡單,首先是一些外部變量的引用聲明,都是不一樣驅動操做接口的集合wpa_driver_XXX_ops變量;而後就是定義一個驅動操做接口集合的數組,根據宏定義添加對應的驅動操做接口集合的變量。

       drvier_XXX.h:這是不一樣驅動接口頭文件,主要聲明瞭操做接口

       drvier_XXX.c:實現操做接口,定義一個操做集合變量,並用上面定義的函數初始化其中的函數指針

       注意:下面要搞清楚wpa_supplicant守護進程是如何操做,最後調用驅動接口集合中的函數的;要知道wpa_supplicant是爲不一樣驅動 和操做系統具備更好移植性而被設計的,以便在wpa_supplicant層不用實現驅動的具體接口就能夠添加新的驅動程序;在 wpa_supplicant結構中有一個wpa_drv_ops類型的drvier成員,在wpa_supplicant進程中,常常經過 Wpa_supplicant_XXX函數傳遞wpa_supplicant實例指針wpa_s參數給wpa_drv_XXX函數來調用它,在 wpa_drv_XX中會經過wpa_s->driver->XXX()的流程來調用通用驅動接口,簡單纔是生活的真相,可android始 終讓我感到真相仍是高不可攀。

 

 12)WifiWatchdogService:

首 先聲明瞭兩個主要變量mWifiStateTracker,mWifiManager,須要這兩個類對象來完成具體的控制工做,在 WifiWatchdogService的構造函數中,建立了這兩個類,並經過regesterForWifiBroadcasts()註冊了 BroadcastReceiver,BroadcastReceiver是用來獲取網絡變化的,而後啓動了一個WatchdogTread線程,用來處 理從WifiStateTracker接收到的消息。

 

frameworks/base/services/java/com/android/server/WifiWatchdogService.java

WifiWatchdogService(Context context,WifiStateTracker wifiStateTracker) {

       mContext = context;

       mContentResolver = context.getContentResolver();

       mWifiStateTracker =wifiStateTracker;

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

       

       createThread();

       

       // The content observer to listen needs a handler, which createThreadcreates

       registerForSettingsChanges();

       if (isWatchdogEnabled()) {

           registerForWifiBroadcasts();

       }

       

       if (V) {

           myLogV("WifiWatchdogService: Created");

       }

    }

       WifiWatchdogHandler繼承了handler類,成爲一個消息處理類,定義handlemessage()函數,處理消息隊列上的消息。

 

二,wpa_supplicant再解析

1)wpa_ctrl.h:

       該文件中並無定義structwpa_ctrl結構,由於在其餘包含該文件的.c文件中不能直接使用該結構的成員,這裏主要聲明瞭幾個用於使用 socket通訊的函數接口,包 括:wpa_ctrl_open,wpa_ctrl_close,wpa_ctrl_attach,wpa_ctrl_detach,wpa_ctrl_cleanup,wpa_ctrl_recv,wpa_ctrl_request, wpa_ctrl_pending, wpa_ctrl_get_fd 等函數。

       這些函數的功能從名字上能看出,open函數就是建立一個socket接口,並綁定鏈接wpa_supplicant,attach函數用於定義一個控制 接口爲監聽接口,pending函數用於查詢有無消息可讀處理,request和recv分別用來發送和讀取消息。

       其實,這裏就是一個使用socket通訊的封裝,具體內容能夠參考socket編程。

 

2)wpa_ctrl.c:

       這裏首先定義了一個wpa_ctrl結構,裏面根據UDP,UNIX和命名管道三種domain類型來定義通訊實體:

struct wpa_ctrl {

#ifdefCONFIG_CTRL_IFACE_UDP

       int s;

       struct sockaddr_in local;

       struct sockaddr_in dest;

       char *cookie;

#endif /*CONFIG_CTRL_IFACE_UDP */

#ifdefCONFIG_CTRL_IFACE_UNIX

       int s;

       struct sockaddr_un local;

       struct sockaddr_un dest;

#endif /*CONFIG_CTRL_IFACE_UNIX */

#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE

       HANDLE pipe;

#endif /*CONFIG_CTRL_IFACE_NAMED_PIPE */

};

       而後是根據上面三個類型分別實現了wpa_ctrl.h中聲明的接口函數,這裏就不作介紹了。

 

3)wpa_supplicant.h:

       首先,定義了一個枚舉wpa_event_type,羅列了系列wpa的事件類型,而後就是wpa_event_data類型,隨後是兩個函數:wpa_supplicant_event和wpa_supplicant_rx_eapol。

wpa_supplicant.c: 首先定義一個驅動操做數組externstruct wpa_driver_ops *wpa_supplicant_drivers[],而後是系列wpa_supplicant_XXX()函數,不少函數裏面調用 wpa_drv_XXX()函數,這些函數是wpa_supplicant_i.h中實現的函數。幾乎每一個函數都須要一個wpa_supplicant結 構,對其進行全部的控制和通訊操做。

 

4)wpa_supplicant_i.h:

       開頭定義了幾個結構, 如:wpa_blacklist,wpa_interface,wpa_params,wpa_global,wpa_client_mlme和 wpa_supplicant等結構,其中wpa_supplicant最爲重要,wpa_supplicant結構裏有一個重要的driver成員,它 是wpa_driver_ops類型,能夠被用來調用抽象層的接口。

       接下來是系列函數聲明,這些函數聲明在wpa_supplicant.c中實現,而後就是wpa_drv_XXX函數,這些函數就是在 wpa_supplicant.c中被wpa_supplicant_xxx函數調用的,而這些wpa_drv_xxx函數也都有一個 wpa_supplicant結構的變量指針,用來調用封裝的抽象接口。

       這裏要注意的是:在wpa_suppliant.c文件中定義的不少函數是在該頭文件中聲明的,而不是在wpa_supplicant.h中聲明的。

 

5)driver.h:

       該文件中定義了一個重要的數據結構:wpa_scan_result,這是一個從驅動發來的數據被封裝成的通用的掃描結果數據結構,每一個驅動結構的實現都 要遵循的掃描結果格式,好比driver_wext.c中的實現。後面還有定義了不少的數據結構,這裏不具表。

       文件中最重要的一個數據結構是:wpa_driver_ops,這是全部驅動接口層必須爲之實現的API,是全部驅動類型的一個接口封裝包,wpa_supplicant就是經過該接口來和驅動交互的。

 

6)driver_wext.h:

       該文件很簡單,就是聲明瞭該驅動的一些對應驅動API接口的函數。

driver_wext.c:

       此文件就是實現了上面的一些函數,最後初始化了一個wpa_drv_ops變量。

 

三,Wifi模塊解析

1)框架分析

 

 

圖示1:Wifi框架

       首先,用戶程序使用WifiManager類來管理Wifi模塊,它可以得到Wifi模塊的狀態,配置和控制Wifi模塊,而全部這些操做都要依賴Wifiservice類來實現。

       WifiService和WifiMonitor類是Wifi框架的核心,如圖所示。下面先來看看WifiService是何時,怎麼被建立和初始化的。

       在systemServer啓動以後,它會建立一個ConnectivityServer對象,這個對象的構造函數會建立一個WifiService的實例,代碼以下所示:

 

framework/base/services/java/com/android/server/ConnectivityService.java

{

……

case ConnectivityManager.TYPE_WIFI:

               if (DBG) Slog.v(TAG, "Starting Wifi Service.");

               WifiStateTracker wst = new WifiStateTracker(context, mHandler);                             //建立WifiStateTracker實例

                WifiService wifiService = newWifiService(context, wst);//建立WifiService實例

               ServiceManager.addService(Context.WIFI_SERVICE, wifiService);           //向服務管理系統添加Wifi服務

               wifiService.startWifi();     //啓動Wifi

               mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;

                wst.startMonitoring(); //啓動WifiMonitor中的WifiThread線程

……

}

       WifiService的主要工做:WifiMonitor和Wpa_supplicant的啓動和關閉,向Wpa_supplicant發送命令。

       WifiMonitor的主要工做:阻塞監聽並接收來自Wpa_supplicant的消息,而後發送給WifiStateTracker。

       上面兩個線程經過AF_UNIX套接字和Wpa_supplicant通訊,在通訊過程當中有兩種鏈接方式:控制鏈接和監聽鏈接。它們建立代碼以下:

ctrl_conn =wpa_ctrl_open(ifname);

.. .. ..

 monitor_conn = wpa_ctrl_open(ifname);

 

 

2)Wifi啓動流程

       (1)使能Wifi

       要想使用Wifi模塊,必須首先使能Wifi,當你第一次按下Wifi使能按鈕時,WirelessSettings會實例化一個WifiEnabler對象,實例化代碼以下:

 

packages/apps/settings/src/com/android/settings/WirelessSettings.java

protected void onCreate(Bundle savedInstanceState) {

       super.onCreate(savedInstanceState);

……

              CheckBoxPreferencewifi = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI);

              mWifiEnabler= new WifiEnabler(this, wifi);

……

}

       WifiEnabler類的定義大體以下,它實現了一個監聽接口,當WifiEnabler對象被初始化後,它監聽到你按鍵的動做,會調用響應函數 onPreferenceChange(),這個函數會調用WifiManager的setWifiEnabled()函數。

public class WifiEnabler implementsPreference.OnPreferenceChangeListener {

……

public boolean onPreferenceChange(Preference preference,Object value) {

        booleanenable = (Boolean) value;

……

if (mWifiManager.setWifiEnabled(enable)) {

                mCheckBox.setEnabled(false);

……

}

……

}

       咱們都知道Wifimanager只是個服務代理,因此它會調用WifiService的setWifiEnabled()函數,而這個函數會調用 sendEnableMessage()函數,瞭解android消息處理機制的都知道,這個函數最終會給本身發送一個 MESSAGE_ENABLE_WIFI的消息,被WifiService裏面定義的handlermessage()函數處理,會調用 setWifiEnabledBlocking()函數。下面是調用流程:

 

mWifiEnabler.onpreferencechange()=>mWifiManage.setWifienabled()=>mWifiService.setWifiEnabled()=>mWifiService.sendEnableMessage()=>mWifiService.handleMessage()=>mWifiService.setWifiEnabledBlocking().

 

在setWifiEnabledBlocking()函數中主要作以下工做:加載Wifi驅動,啓動wpa_supplicant,註冊廣播接收器,啓動WifiThread監聽線程。代碼以下:

……

if (enable) {

           if (!mWifiStateTracker.loadDriver()) {

               Slog.e(TAG, "Failed toload Wi-Fi driver.");

               setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);

                return false;

           }

           if (!mWifiStateTracker.startSupplicant()) {

                mWifiStateTracker.unloadDriver();

                Slog.e(TAG, "Failed tostart supplicant daemon.");

               setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);

                return false;

           }

 

           registerForBroadcasts();

           mWifiStateTracker.startEventLoop();

……

       至此,Wifi使能結束,自動進入掃描階段。

 

(2) 掃描AP

       當驅動加載成功後,若是配置文件的AP_SCAN = 1,掃描會自動開始,WifiMonitor將會從supplicant收到一個消息EVENT_DRIVER_STATE_CHANGED,調用 handleDriverEvent(),而後調用mWifiStateTracker.notifyDriverStarted(),該函數向消息隊列 添加EVENT_DRIVER_STATE_CHANGED,handlermessage()函數處理消息時調用scan()函數,並經過 WifiNative將掃描命令發送到wpa_supplicant。

 

Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

private void handleDriverEvent(Stringstate) {

           if (state == null) {

                return;

           }

           if (state.equals("STOPPED")) {

               mWifiStateTracker.notifyDriverStopped();

           } else if (state.equals("STARTED")) {

                mWifiStateTracker.notifyDriverStarted();

           } else if (state.equals("HANGED")) {

                mWifiStateTracker.notifyDriverHung();

           }

       }

 

Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

case EVENT_DRIVER_STATE_CHANGED:

        

                switch(msg.arg1) {

                case DRIVER_STARTED:

                    /**

                     *Set the number of allowed radio channels according

                     *to the system setting, since it gets reset by the

                     *driver upon changing to the STARTED state.

                     */

                    setNumAllowedChannels();

                   synchronized (this) {

                       if (mRunState == RUN_STATE_STARTING) {

                           mRunState = RUN_STATE_RUNNING;

                           if (!mIsScanOnly) {

                                reconnectCommand();

                           } else {

                                // In somesituations, supplicant needs to be kickstarted to

                                // start thebackground scanning

                                scan(true);

                           }

                       }

                    }

                   break;             

 

上面是啓動Wifi時,自動進行的AP的掃描,用戶固然也能夠手動掃描AP,這部分實如今WifiService裏面,WifiService經過startScan()接口函數發送掃描命令到supplicant。

 

Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

public boolean startScan(booleanforceActive) {

       enforceChangePermission();

 

       switch (mWifiStateTracker.getSupplicantState()) {

           case DISCONNECTED:

           case INACTIVE:

           case SCANNING:

           case DORMANT:

                break;

           default:

               mWifiStateTracker.setScanResultHandling(

                       WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);

                break;

       }

       return mWifiStateTracker.scan(forceActive);

    }

       而後下面的流程同上面的自動掃描,咱們來分析一下手動掃描從哪裏開始的。咱們應該知道手動掃描是經過菜單鍵的掃描鍵來響應的,而響應該動做的應該是 WifiSettings類中Scanner類的handlerMessage()函數,它調用WifiManager的 startScanActive(),這才調用WifiService的startScan()。

 

packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

public boolean onCreateOptionsMenu(Menu menu) {

       menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)

               .setIcon(R.drawable.ic_menu_scan_network);

       menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)

               .setIcon(android.R.drawable.ic_menu_manage);

       return super.onCreateOptionsMenu(menu);

    }

 

       當按下菜單鍵時,WifiSettings就會調用這個函數繪製菜單。若是選擇掃描按鈕,WifiSettings會調用onOptionsItemSelected()。

 

packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

public booleanonOptionsItemSelected(MenuItem item) {

       switch (item.getItemId()) {

           case MENU_ID_SCAN:

                if(mWifiManager.isWifiEnabled()) {

                    mScanner.resume();

                }

                return true;

           case MENU_ID_ADVANCED:

                startActivity(new Intent(this,AdvancedSettings.class));

                return true;

       }

       return super.onOptionsItemSelected(item);

}

 

private class Scanner extends Handler {

       private int mRetry = 0;

 

       void resume() {

           if (!hasMessages(0)) {

                sendEmptyMessage(0);

           }

       }

 

       void pause() {

           mRetry = 0;

            mAccessPoints.setProgress(false);

           removeMessages(0);

       }

 

       @Override

       public void handleMessage(Message message) {

           if (mWifiManager.startScanActive()){

                mRetry = 0;

           } else if (++mRetry >= 3) {

                mRetry = 0;

               Toast.makeText(WifiSettings.this, R.string.wifi_fail_to_scan,

                       Toast.LENGTH_LONG).show();

                return;

           }

           mAccessPoints.setProgress(mRetry != 0);

           sendEmptyMessageDelayed(0, 6000);

       }

    }

      

這裏的mWifiManager.startScanActive()就會調用WifiService裏的startScan()函數,下面的流程和上面的同樣,這裏不贅述。

當supplicant完成了這個掃描命令後,它會發送一個消息給上層,提醒他們掃描已經完成,WifiMonitor會接收到這消息,而後再發送給WifiStateTracker。

 

Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

void handleEvent(int event, String remainder) {

            switch (event) {

                caseDISCONNECTED:

                   handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);

                    break;

 

                case CONNECTED:

                   handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);

                    break;

 

                case SCAN_RESULTS:

                    mWifiStateTracker.notifyScanResultsAvailable();

                    break;

 

                case UNKNOWN:

                    break;

            }

        }

WifiStateTracker將會廣播SCAN_RESULTS_AVAILABLE_ACTION消息:

 

Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

public voidhandleMessage(Message msg) {

        Intent intent;

……

case EVENT_SCAN_RESULTS_AVAILABLE:

                if(ActivityManagerNative.isSystemReady()) {

                    mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

                }

                sendScanResultsAvailable();

                /**

                 * On receiving the first scanresults after connecting to

                 * the supplicant, switch scanmode over to passive.

                 */

                setScanMode(false);

                break;

……

       因爲WifiSettings類註冊了intent,可以處理SCAN_RESULTS_AVAILABLE_ACTION消息,它會調用handleEvent(),調用流程以下所示。

 

WifiSettings.handleEvent() =>WifiSettings.updateAccessPoints() => mWifiManager.getScanResults() => mService.getScanResults()=> mWifiStateTracker.scanResults() => WifiNative.scanResultsCommand()……

 

將獲取AP列表的命令發送到supplicant,而後supplicant經過Socket發送掃描結果,由上層接收並顯示。這和前面的消息獲取流程基本相同。

 

(3)配置,鏈接AP

當用戶選擇一個活躍的AP時,WifiSettings響應打開一個對話框來配置AP,好比加密方法和鏈接AP的驗證模式。配置好AP後,WifiService添加或更新網絡鏈接到特定的AP。

 

packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

public booleanonPreferenceTreeClick(PreferenceScreen screen, Preference preference) {

       if (preference instanceof AccessPoint) {

           mSelected = (AccessPoint) preference;

           showDialog(mSelected, false);

       } else if (preference == mAddNetwork) {

           mSelected = null;

           showDialog(null, true);

       } else if (preference == mNotifyOpenNetworks) {

           Secure.putInt(getContentResolver(),

                   Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,

                   mNotifyOpenNetworks.isChecked() ? 1 : 0);

       } else {

           return super.onPreferenceTreeClick(screen, preference);

       }

       return true;

    }

 

       配置好之後,當按下「Connect Press」時,WifiSettings經過發送LIST_NETWORK命令到supplicant來檢查該網絡是否配置。若是沒有該網絡或沒有配置 它,WifiService調用addorUpdateNetwork()函數來添加或更新網絡,而後發送命令給supplicant,鏈接到這個網絡。 下面是從響應鏈接按鈕到WifiService發送鏈接命令的代碼:

 

packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

public void onClick(DialogInterfacedialogInterface, int button) {

       if (button == WifiDialog.BUTTON_FORGET && mSelected != null) {

           forget(mSelected.networkId);

       } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog !=null) {

           WifiConfiguration config = mDialog.getConfig();

 

           if (config == null) {

                if (mSelected != null&& !requireKeyStore(mSelected.getConfig())) {

                    connect(mSelected.networkId);

                }

           } else if (config.networkId != -1) {

                if (mSelected != null) {

                    mWifiManager.updateNetwork(config);

                    saveNetworks();

                }

           } else {

                int networkId =mWifiManager.addNetwork(config);

                if (networkId != -1) {

                   mWifiManager.enableNetwork(networkId, false);

                    config.networkId =networkId;

                    if (mDialog.edit || requireKeyStore(config)){

                        saveNetworks();

                    } else {

                        connect(networkId);

                    }

                }

           }

       }

    }

      

      

Frameworks\base\wifi\java\android\net\wifi\WifiManager.java

       public intupdateNetwork(WifiConfiguration config) {

        if(config == null || config.networkId < 0) {

           return -1;

        }

        return addOrUpdateNetwork(config);

}

private intaddOrUpdateNetwork(WifiConfiguration config) {

       try {

           return mService.addOrUpdateNetwork(config);

       } catch (RemoteException e) {

           return -1;

       }

    }

 

WifiService.addOrUpdateNetwork()經過調用mWifiStateTracker.setNetworkVariable()將鏈接命令發送到Wpa_supplicant。

 

(4)獲取IP地址

當鏈接到supplicant後,WifiMonitor就會通知WifiStateTracker。

 

Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

Public void Run(){

if (connectToSupplicant()) {

                // Send a message indicatingthat it is now possible to send commands

                // to the supplicant

                mWifiStateTracker.notifySupplicantConnection();

           } else {

               mWifiStateTracker.notifySupplicantLost();

                return;

           }

……

}

 

WifiStateTracker發送EVENT_SUPPLICANT_CONNECTION消息到消息隊列,這個消息有本身的handlermessage()函數處理,它會啓動一個DHCP線程,而這個線程會一直等待一個消息事件,來啓動DHCP協議分配IP地址。

 

frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

void notifySupplicantConnection() {

       sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);

}

 

public void handleMessage(Message msg) {

       Intent intent;

 

       switch (msg.what) {

           case EVENT_SUPPLICANT_CONNECTION:

             ……

             HandlerThread dhcpThread = newHandlerThread("DHCP Handler Thread");

                dhcpThread.start();

                mDhcpTarget = newDhcpHandler(dhcpThread.getLooper(), this);

……

……

}

當Wpa_supplicant鏈接到AP後,它會發送一個消息給上層來通知鏈接成功,WifiMonitor會接受到這個消息並上報給WifiStateTracker。

 

Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

void handleEvent(int event, String remainder) {

           switch (event) {

               case DISCONNECTED:

                   handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);

                   break;

 

               case CONNECTED:

                   handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);

                   break;

                ……

}

 

private voidhandleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {

        StringBSSID = null;

        intnetworkId = -1;

        if(newState == NetworkInfo.DetailedState.CONNECTED) {

           Matcher match = mConnectedEventPattern.matcher(data);

            if(!match.find()) {

               if (Config.LOGD) Log.d(TAG, "Could not find BSSID in CONNECTEDevent string");

            }else {

               BSSID = match.group(1);

               try {

                   networkId = Integer.parseInt(match.group(2));

               } catch (NumberFormatException e) {

                   networkId = -1;

                }

            }

        }

        mWifiStateTracker.notifyStateChange(newState,BSSID, networkId);

}

      

void notifyStateChange(DetailedState newState, StringBSSID, int networkId) {

        Messagemsg = Message.obtain(

           this, EVENT_NETWORK_STATE_CHANGED,

            newNetworkStateChangeResult(newState, BSSID, networkId));

       msg.sendToTarget();

    }

 

caseEVENT_NETWORK_STATE_CHANGED:

……

configureInterface();

……

 

private void configureInterface() {

       checkPollTimer();

        mLastSignalLevel = -1;

        if(!mUseStaticIp) {          //使用DHCP線程動態IP

            if(!mHaveIpAddress && !mObtainingIpAddress) {

               mObtainingIpAddress = true;

 

                                   //發送啓動DHCP線程獲取IP

                mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);

            }

        } else {        //使用靜態IP,IP信息從mDhcpInfo中獲取

            intevent;

            if(NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {

               mHaveIpAddress = true;

               event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;

               if (LOCAL_LOGD) Log.v(TAG, "Static IP configurationsucceeded");

            }else {

               mHaveIpAddress = false;

               event = EVENT_INTERFACE_CONFIGURATION_FAILED;

               if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");

            }

           sendEmptyMessage(event);           //發送IP得到成功消息事件

        }

    }

              DhcpThread獲取EVENT_DHCP_START消息事件後,調用handleMessage()函數,啓動DHCP獲取IP地址的服務。

 

public void handleMessage(Message msg) {

            intevent;

switch (msg.what) {

               case EVENT_DHCP_START:

 

……

Log.d(TAG, "DhcpHandler: DHCP requeststarted");

 

//啓動一個DHCPclient的精靈進程,爲mInterfaceName請求分配一個IP地//址

    if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {

     event= EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;

         if(LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");

    } else {

           event= EVENT_INTERFACE_CONFIGURATION_FAILED;

           Log.i(TAG,"DhcpHandler: DHCP request failed: " +

                            NetworkUtils.getDhcpError());

        }

……

}

這 裏調用了一個NetworkUtils.runDhcp()函數,NetworkUtils類是一個網絡服務的輔助類,它主要定義了一些本地接口,這些接 口會經過他們的JNI層android_net_NetUtils.cpp文件和DHCP client通訊,並獲取IP地址。

至此,IP地址獲取完畢,Wifi啓動流程結束。

相關文章
相關標籤/搜索