最 近研究Wifi模塊,查了不少的相關(guān)資料,但發(fā)現(xiàn)基本上是基于android2.0版本的的分析,而現(xiàn)在研發(fā)的android移動平臺基本上都是2.3的 版本,跟2.0版本的差別,在Wifi模塊上也是顯而易見的。2.3版本W(wǎng)ifi模塊沒有了WifiLayer,之前的WifiLayer主要負責(zé)一些復(fù) 雜的Wifi功能,如AP選擇等以提供給用戶自定義,而新的版本里面的這塊內(nèi)容基本上被WifiSettings所代替。
本文就是基于android2.3版本的Wifi分析,主要分為兩部分來分別說明:
(1) Wifi模塊相關(guān)文件的解析
(2) Wpa_supplicant解析
(3) Wifi的啟動流程(有代碼供參考分析)
一,Wifi模塊相關(guān)文件解析
1) wifisettings.java
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
該類數(shù)據(jù)部分主要定義了下面幾個類的變量:
{
private final IntentFilter mFilter;
//廣播接收器,用來接收消息并做響應(yīng)的處理工作
privatefinal BroadcastReceiver mReceiver;
//這是一個掃描類,會在用戶手動掃描 AP時被調(diào)用
privatefinal Scanner mScanner;
private WifiInfo mLastInfo;
//服務(wù)代理端,作為WifiService對外的接口類呈現(xiàn)
privateWifiManager mWifiManager;
//這個類主要實現(xiàn)Wifi的開閉工作
privateWifiEnabler mWifiEnabler;
//AP
private AccessPoint mSelected;
private WifiDialog mDialog;
……
}
wifiSettings類的構(gòu)造函數(shù)的主要工作:定義了一個IntentFilter(Intent過濾器)變量,并添加了六個動作,(了解 Android的intent機制的同學(xué)都知道什么意思,不明白的同學(xué)參考Intent機制的資料)接著定義一個廣播接收器,并有相應(yīng)的消息處理函數(shù),下 面是該構(gòu)造函數(shù)的定義:
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); //事件處理函數(shù)
}
};
mScanner= new Scanner(); //手動掃描類
}
在廣播接收器中的相應(yīng)函數(shù)onReceive函數(shù)中有個handleEvent函數(shù),它就是用來處理廣播接收器接受到的intent消息的,它的功能是根 據(jù)intent消息中的動作類型,來執(zhí)行相應(yīng)的操作,每一種動作對應(yīng)了activity的一項消息處理能力。
在oncreate函數(shù)中實例化了mWifiManager和mWifiEnabler兩個類,這兩個類對wifiSettings來說至關(guān)重要,它后面的定義的一系列函數(shù)都是通過調(diào)用這兩個類的相應(yīng)接口來實現(xiàn)的。
……
mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
mWifiEnabler = new WifiEnabler(this,
(CheckBoxPreference) findPreference("enable_wifi"));
……
WifiSettings中還定義了顯示菜單和響應(yīng)菜單鍵的函數(shù),即onCreateOptionsMenu()和 onOptionsItemSelected();還有響應(yīng)配置對話框中按鍵的onClick()函數(shù);最后定義了Scanner類,它是一個 handler的繼承類,實現(xiàn)了消息處理函數(shù),用于處理手動掃描的動作。
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用來接收底層發(fā)來的消息,mCheckBox用來改變UI的狀態(tài)。
該 類中定義了幾個重要的函數(shù)onPreferenceChange,handleWifiStateChanged和 handleStateChanged,onPreferenceChange用來處理按下的Enbler鍵,它會調(diào)用 mWifiManager.setWifiEnabled(enable),另外兩個用來處理接受的消息事件。
在類的構(gòu)造函數(shù)中,主要做了一下工作:初始化了mContext,mCheckBox,mWifimanager,并且初始化了一個 mIntentFilter變量,添加了三個動作,在構(gòu)造函數(shù)的上面定義了一個廣播接收器,用來接收下層傳來的消息,并根據(jù)intent動作的類型調(diào)用相 應(yīng)的處理函數(shù),這個廣播接收器在onResum函數(shù)中被注冊。
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);
}
這 里可以總結(jié)為:如果上層需要監(jiān)聽或收到下層的消息,那么就要通過定義一個BroadcastReciever,并將它注冊,當(dāng)然在接受到消息后應(yīng)該有處理 消息的函數(shù),然后在onReciever函數(shù)中根據(jù)消息調(diào)用相應(yīng)的處理函數(shù),這里的消息通知機制是Intent,在BroadcastReciever類 的onReciever函數(shù)的參數(shù)中可以看出。[!--empirenews.page--]
該類成員函數(shù)的也是通過調(diào)用mWifimanager的接口來實現(xiàn)的。
3) WifiManager:
frameworks/base/wifi/java/android/net/wifi/WifiManager.java
兩個重要的數(shù)據(jù)成員:
//WifiService
IWifiManager mService;
HandlermHandler;
IWifiManager mService和HandlermHandler,這個類擁有了一個WifiService實例,就可以通過它進行一系列的調(diào) 用;WifiManager中定義了的wifi和ap的狀態(tài),這些狀態(tài)會在其他很多類中有使用;然后定義了大量的函數(shù),這些函數(shù)幾乎都是對 WifiService接口函數(shù)的封裝,直接調(diào)用WifiService的函數(shù)。
該類的構(gòu)造函數(shù)很簡單:
public WifiManager(IWifiManager service,Handler handler) {
mService = service;
mHandler = handler;
}
該 類中還定義了一個WifiLock類,這個類用來保證在有應(yīng)用程序使用Wifi無線電傳輸數(shù)據(jù)時,wifiradio可用,即當(dāng)一個應(yīng)用程序使用wifi 的radio進行無線電數(shù)據(jù)傳輸時,就要先獲得這個鎖,如果該鎖已被其他程序占有,就要等到該鎖被釋放后才能獲得,只用當(dāng)所有持有該鎖的程序都釋放該鎖 后,才能關(guān)閉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中的幾個重要的數(shù)據(jù)成員。
在 接下來的構(gòu)造函數(shù)中初始化了mWifiStateTracker,mContext,然后動態(tài)生成mWifiThread子線程并啟動,在主線程里用 mWifiThread調(diào)用getLooper()函數(shù)獲得線程的looper,來初始化創(chuàng)建一個mWifiHandler對象,這個 WifiHandler在WifiService類的后面有定義,并重載了Handler類的handlermessage()函數(shù),這樣消息就可以在主 線程里被處理了,這是android的handlerthread消息處理機制,可參考相關(guān)資料,這里不予詳述。在構(gòu)造函數(shù)的最后,注冊了兩個廣播接收 器,分別用來ACTION_AIRPLANE_MODE_CHANGED和ACTION_TETHER_STATE_CHANGED這兩個動作,這里是 android的intent消息通知機制,請參考相關(guān)資料,代碼如下:
mContext = context;
mWifiStateTracker = tracker;
mWifiStateTracker.enableRssiPolling(true);
……
HandlerThread wifiThread = newHandlerThread("WifiService");
wifiThread.start();
mWifiHandler = newWifiHandler(wifiThread.getLooper());
……
隨 后定義了一系列的函數(shù),其中有服務(wù)器要發(fā)送的命令的系列函數(shù),它通過mWifiStateTracker成員類調(diào)用自己的的發(fā)送命令的接口(其實就是對本 地接口的一個封裝),最后通過適配層發(fā)送命令給wpa_supplicant,而事件處理只到WifiStateTracker層被處理。
要 注意的是,在WifiService中,定義了一些函數(shù)來創(chuàng)建消息,并通過mWifiHandler將消息發(fā)送到消息隊列上,然后在 mHandlerThread線程體run()分發(fā)\處理消息,在主線程中被mWifiHandler的handlerMessage()函數(shù)處理,最后 調(diào)用mWifiStateTracker的對應(yīng)函數(shù)來實現(xiàn)的。這里我也不明白為什么WifiService不直接調(diào)用mWifiStateTracker 對應(yīng)的函數(shù),還要通過消息處理機制,繞了一圈在調(diào)用,當(dāng)然Google這么做肯定是有它道理的,忘高手指點。
5) WifiStateTracker類:
frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java
NetworkStateTracker繼承了handler類,而WifiStateTracker繼承了NetworkStateTracker類,就是說WifiStateTracker間接繼承了handler類,屬于一個事件處理類。
WifiStateTracker類首先定義了事件日志和事件碼(這里包含了所有可能的事件類型),還定義了如下的重要成員數(shù)據(jù):
//幾個重要的數(shù)據(jù)成員
private WifiMonitor mWifiMonitor; //被啟動用來監(jiān)聽supplicant傳來的消息
private WifiInfo mWifiInfo;
private WifiManager mWM; //服務(wù)代理
private DhcpHandler mDhcpTarget; //IP地址獲取線程
private DhcpInfo mDhcpInfo; //Dhcp的相關(guān)信息都在這里
類的構(gòu)造函數(shù)中,初始化了系列成員變量,包括生成了WifiMonitor的實例,在構(gòu)造函數(shù)中,因為WifiStateTracker是一個handler間接子類,所以他會自動調(diào)用handler的無參構(gòu)造函數(shù),獲得looper和Queue消息隊列。
然后定義了一些設(shè)置supplicant和更新網(wǎng)絡(luò)信息的輔助函數(shù)。
startEventLoop()函數(shù)很重要,用來啟動WifiMonitor線程,進入消息循環(huán)檢測。接著定義了系列通知函數(shù),被 WifiMonitor調(diào)用來向WifiStateTracker傳遞從wpa_supplicant接收到的消息,他會調(diào)用消息發(fā)送函數(shù)到消息隊列,并 被WifiStateTracker的handlermessage()函數(shù)處理。這個handlermessage()函數(shù)就是在隨后被定義的,它主要 是調(diào)用相應(yīng)的輔助函數(shù)完成動作,并可能會將消息封裝后,通過intent機制發(fā)送出去,被上層的UI活動接收處理。
這 里也定義了很多的WfiNative接口函數(shù),這是JNI的本地接口;類DhcpHandler extends Handler{}也是在該類中定義的,它也是一個handler的子類,用來處理DHCP相關(guān)的消息EVENT_DHCP_START,可以想到它和 WifiStateTracker不是共用一個looper。
注意:handleMessage是在該文件中定義的,用來處理經(jīng)WifiMonitor轉(zhuǎn)換過的消息。
6) WifiMonitor
frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java
聲明了一個重要的成員變量:mWifiStateTracker,并在構(gòu)造函數(shù)中由參數(shù)提供初始化,還定義了一系列的可能從wpa_supplicant層接收的事件類型及其名字,這些是消息處理機制的基礎(chǔ)。
startMonitoring()函數(shù),這是一個線程啟動的封裝函數(shù),WifiStateTracker就是通過這個函數(shù)啟動的WifiThread。
這 個重要的類classMonitorThreadextends Thread{};它是一個監(jiān)控進程類,里面有一系列的事件處理函數(shù)和一個重要的Run()函數(shù),run函數(shù)主要流 程:connectToSupplicant()連接精靈進程wpa_supplicant,這里有一個 mWifiStateTracker.notifySupplicantXXX()的調(diào)用,通知上層是否連接成功,然后就是一個輪詢過程,其中調(diào)用了 WifiNative.waitForEvent()本地輪詢函數(shù)接口,并從返回的事件字符串類型中提取事件的名稱,最后通過事件的名稱調(diào)用相應(yīng)的事件處 理函數(shù),并將事件轉(zhuǎn)換成mWifiStateTracker能識別的類型上報。[!--empirenews.page--]
注意:這里的每個事件的捕獲(由wifimonitor完成),最終都會調(diào)用相應(yīng)的mWifiStateTracker的消息通知函數(shù)上報消息;輪詢和事件處理都是在Monitor線程類中實現(xiàn)的。
7)WifiNative
frameworks/base/wifi/java/android/net/wifi/WifiNative.java
里面定義了一個類WifiNative:其中聲明了許多本地接口,可由native的標(biāo)志看出,這是Java代碼和本地庫之間的聯(lián)系接口;
8) android_net_wifi_Wifi.java
frameworks/base/core/jni/
里面定義了許多的JNI的本地代碼實現(xiàn),每個實現(xiàn)中都會調(diào)用wpa_supplicant適配層的接口,通過包含適配層的頭文件wifi.h獲取適配層定 義的接口;后面是一個JNINativeMethod數(shù)組,定義了每個本地接口和本地實現(xiàn)的對應(yīng)關(guān)系;最后有一個注冊函數(shù) regester_XXX_XX(),用以把上面的方法類數(shù)組注冊到系統(tǒng)中。
該類實現(xiàn)了本地接口的相關(guān)功能,并通過調(diào)用wpa的適配層的相關(guān)函數(shù)和wpa_supplicant通信,所以JNI是連接Java框架層和wpa適配層的橋梁.
9)wpa_supplicant適配層,wifi.c:目錄libhardware/wifi/
里面定義很多字符串變量和適配層的接口實現(xiàn),是對wpa_supplicant程序通信的接口封裝,用來完成上層和wpa_supplicant的通信, 頭文件在libhardware/include/hardware下,這里的函數(shù)用來向JNI的本地實現(xiàn)提供調(diào)用接口。
這里的函數(shù),我把它們分為三類函數(shù):
一 類是命令相關(guān)的(控制)函數(shù),就是在JNI層android_XXX_Command()函數(shù)所調(diào)用的::Wifi_Command()函數(shù),調(diào)用流 程:android_XXX_command()=>docommand()=>wifi_command()=> wifi_send_command()=>wpa_ctrl_require()。
二類是監(jiān)聽函數(shù),即Wifi_wait_for_event()函數(shù),調(diào)用流程:android_net_wifi_Waitforevent()=>wifi_wait_for_event()=>wpa_ctrl_recv()。
三類是剩下的函數(shù)。
10)wpa_supplicant與上層的接口,wpa_ctrl.c:external/wpa_supplicant
定義了三類套接字,并分別實現(xiàn)了和wpa_supplicant的通信,因此wpa_supplicant適配層和wpa_supplicant層是通過socket通訊的。
要 是從wifi.c中真的很難看出它和wpa_supplicant有什么關(guān)系,和它聯(lián)系密切的是wpa_ctrl.h文件,這里面定義了一個類 wpa_ctrl,這個類中聲明了兩個Socket套接口,一個是本地一個是要連接的套接口,wpa_ctrl與wpa_supplicant的通信就需 要socket來幫忙了,而wpa_supplicant就是通過調(diào)用wpa_ctrl.h中定義的函數(shù)和wpa_supplicant進行通訊 的,wpa_ctrl類(其實是其中的兩個socket)就是他們之間的橋梁。
11)wpa_supplicant和driver_wext驅(qū)動接口的聯(lián)系:
driver.h:該文件定義了系列結(jié)構(gòu),首先是一個wpa_scan_result結(jié)構(gòu),這是一個掃描結(jié)果的通用格式,里面包含了掃描的所有信息(如 BSSID,SSID,信號質(zhì)量,噪音水平,支持的最大波特率等等信息),每個驅(qū)動接口實現(xiàn)負責(zé)將從驅(qū)動中上傳的掃描信息的格式轉(zhuǎn)換到該通用的掃描信息格 式;然后是一些宏定義和枚舉定義,最后也是最重要的是wpa_driver_ops結(jié)構(gòu),該結(jié)構(gòu)是wpa driver的操作函數(shù)集合,里面有驅(qū)動接口的名稱和很多的函數(shù)指針。
drviers.c:該文件很簡單,首先是一些外部變量的引用聲明,都是不同驅(qū)動操作接口的集合wpa_driver_XXX_ops變量;然后就是定義一個驅(qū)動操作接口集合的數(shù)組,根據(jù)宏定義添加對應(yīng)的驅(qū)動操作接口集合的變量。
drvier_XXX.h:這是不同驅(qū)動接口頭文件,主要聲明了操作接口
drvier_XXX.c:實現(xiàn)操作接口,定義一個操作集合變量,并用上面定義的函數(shù)初始化其中的函數(shù)指針
注意:下面要搞清楚wpa_supplicant守護進程是如何操作,最后調(diào)用驅(qū)動接口集合中的函數(shù)的;要知道wpa_supplicant是為不同驅(qū)動 和操作系統(tǒng)具有更好移植性而被設(shè)計的,以便在wpa_supplicant層不用實現(xiàn)驅(qū)動的具體接口就可以添加新的驅(qū)動程序;在 wpa_supplicant結(jié)構(gòu)中有一個wpa_drv_ops類型的drvier成員,在wpa_supplicant進程中,經(jīng)常通過 Wpa_supplicant_XXX函數(shù)傳遞wpa_supplicant實例指針wpa_s參數(shù)給wpa_drv_XXX函數(shù)來調(diào)用它,在 wpa_drv_XX中會通過wpa_s->driver->XXX()的流程來調(diào)用通用驅(qū)動接口,簡單才是生活的真相,可android始 終讓我感到真相還是遙不可及。
12)WifiWatchdogService:
首 先聲明了兩個主要變量mWifiStateTracker,mWifiManager,需要這兩個類對象來完成具體的控制工作,在 WifiWatchdogService的構(gòu)造函數(shù)中,創(chuàng)建了這兩個類,并通過regesterForWifiBroadcasts()注冊了 BroadcastReceiver,BroadcastReceiver是用來獲取網(wǎng)絡(luò)變化的,然后啟動了一個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()函數(shù),處理消息隊列上的消息。
二,wpa_supplicant再解析
1)wpa_ctrl.h:
該文件中并沒有定義structwpa_ctrl結(jié)構(gòu),因為在其他包含該文件的.c文件中不能直接使用該結(jié)構(gòu)的成員,這里主要聲明了幾個用于使用 socket通信的函數(shù)接口,包 括: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 等函數(shù)。[!--empirenews.page--]
這些函數(shù)的功能從名字上能看出,open函數(shù)就是創(chuàng)建一個socket接口,并綁定連接wpa_supplicant,attach函數(shù)用于定義一個控制 接口為監(jiān)聽接口,pending函數(shù)用于查詢有無消息可讀處理,request和recv分別用來發(fā)送和讀取消息。
其實,這里就是一個使用socket通信的封裝,具體內(nèi)容可以參考socket編程。
2)wpa_ctrl.c:
這里首先定義了一個wpa_ctrl結(jié)構(gòu),里面根據(jù)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 */
};
然后是根據(jù)上面三個類型分別實現(xiàn)了wpa_ctrl.h中聲明的接口函數(shù),這里就不做介紹了。
3)wpa_supplicant.h:
首先,定義了一個枚舉wpa_event_type,羅列了系列wpa的事件類型,然后就是wpa_event_data類型,隨后是兩個函數(shù):wpa_supplicant_event和wpa_supplicant_rx_eapol。
wpa_supplicant.c: 首先定義一個驅(qū)動操作數(shù)組externstruct wpa_driver_ops *wpa_supplicant_drivers[],然后是系列wpa_supplicant_XXX()函數(shù),很多函數(shù)里面調(diào)用 wpa_drv_XXX()函數(shù),這些函數(shù)是wpa_supplicant_i.h中實現(xiàn)的函數(shù)。幾乎每個函數(shù)都需要一個wpa_supplicant結(jié) 構(gòu),對其進行所有的控制和通信操作。
4)wpa_supplicant_i.h:
開頭定義了幾個結(jié)構(gòu), 如:wpa_blacklist,wpa_interface,wpa_params,wpa_global,wpa_client_mlme和 wpa_supplicant等結(jié)構(gòu),其中wpa_supplicant最為重要,wpa_supplicant結(jié)構(gòu)里有一個重要的driver成員,它 是wpa_driver_ops類型,可以被用來調(diào)用抽象層的接口。
接下來是系列函數(shù)聲明,這些函數(shù)聲明在wpa_supplicant.c中實現(xiàn),然后就是wpa_drv_XXX函數(shù),這些函數(shù)就是在 wpa_supplicant.c中被wpa_supplicant_xxx函數(shù)調(diào)用的,而這些wpa_drv_xxx函數(shù)也都有一個 wpa_supplicant結(jié)構(gòu)的變量指針,用來調(diào)用封裝的抽象接口。
這里要注意的是:在wpa_suppliant.c文件中定義的很多函數(shù)是在該頭文件中聲明的,而不是在wpa_supplicant.h中聲明的。
5)driver.h:
該文件中定義了一個重要的數(shù)據(jù)結(jié)構(gòu):wpa_scan_result,這是一個從驅(qū)動發(fā)來的數(shù)據(jù)被封裝成的通用的掃描結(jié)果數(shù)據(jù)結(jié)構(gòu),每個驅(qū)動結(jié)構(gòu)的實現(xiàn)都 要遵循的掃描結(jié)果格式,比如driver_wext.c中的實現(xiàn)。后面還有定義了很多的數(shù)據(jù)結(jié)構(gòu),這里不具表。
文件中最重要的一個數(shù)據(jù)結(jié)構(gòu)是:wpa_driver_ops,這是所有驅(qū)動接口層必須為之實現(xiàn)的API,是所有驅(qū)動類型的一個接口封裝包,wpa_supplicant就是通過該接口來和驅(qū)動交互的。
6)driver_wext.h:
該文件很簡單,就是聲明了該驅(qū)動的一些對應(yīng)驅(qū)動API接口的函數(shù)。
driver_wext.c:
此文件就是實現(xiàn)了上面的一些函數(shù),最后初始化了一個wpa_drv_ops變量。
三,Wifi模塊解析
1)框架分析
圖示1:Wifi框架
首先,用戶程序使用WifiManager類來管理Wifi模塊,它能夠獲得Wifi模塊的狀態(tài),配置和控制Wifi模塊,而所有這些操作都要依賴Wifiservice類來實現(xiàn)。
WifiService和WifiMonitor類是Wifi框架的核心,如圖所示。下面先來看看WifiService是什么時候,怎么被創(chuàng)建和初始化的。
在systemServer啟動之后,它會創(chuàng)建一個ConnectivityServer對象,這個對象的構(gòu)造函數(shù)會創(chuàng)建一個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); //創(chuàng)建WifiStateTracker實例
WifiService wifiService = newWifiService(context, wst);//創(chuàng)建WifiService實例
ServiceManager.addService(Context.WIFI_SERVICE, wifiService); //向服務(wù)管理系統(tǒng)添加Wifi服務(wù)
wifiService.startWifi(); //啟動Wifi
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
wst.startMonitoring(); //啟動WifiMonitor中的WifiThread線程
……
}
WifiService的主要工作:WifiMonitor和Wpa_supplicant的啟動和關(guān)閉,向Wpa_supplicant發(fā)送命令。
WifiMonitor的主要工作:阻塞監(jiān)聽并接收來自Wpa_supplicant的消息,然后發(fā)送給WifiStateTracker。
上面兩個線程通過AF_UNIX套接字和Wpa_supplicant通信,在通信過程中有兩種連接方式:控制連接和監(jiān)聽連接。它們創(chuàng)建代碼如下:
ctrl_conn =wpa_ctrl_open(ifname);
.. .. ..
monitor_conn = wpa_ctrl_open(ifname);
2)Wifi啟動流程
(1)使能Wifi
要想使用Wifi模塊,必須首先使能Wifi,當(dāng)你第一次按下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類的定義大致如下,它實現(xiàn)了一個監(jiān)聽接口,當(dāng)WifiEnabler對象被初始化后,它監(jiān)聽到你按鍵的動作,會調(diào)用響應(yīng)函數(shù) onPreferenceChange(),這個函數(shù)會調(diào)用WifiManager的setWifiEnabled()函數(shù)。[!--empirenews.page--]
public class WifiEnabler implementsPreference.OnPreferenceChangeListener {
……
public boolean onPreferenceChange(Preference preference,Object value) {
booleanenable = (Boolean) value;
……
if (mWifiManager.setWifiEnabled(enable)) {
mCheckBox.setEnabled(false);
……
}
……
}
我們都知道Wifimanager只是個服務(wù)代理,所以它會調(diào)用WifiService的setWifiEnabled()函數(shù),而這個函數(shù)會調(diào)用 sendEnableMessage()函數(shù),了解android消息處理機制的都知道,這個函數(shù)最終會給自己發(fā)送一個 MESSAGE_ENABLE_WIFI的消息,被WifiService里面定義的handlermessage()函數(shù)處理,會調(diào)用 setWifiEnabledBlocking()函數(shù)。下面是調(diào)用流程:
mWifiEnabler.onpreferencechange()=>mWifiManage.setWifienabled()=>mWifiService.setWifiEnabled()=>mWifiService.sendEnableMessage()=>mWifiService.handleMessage()=>mWifiService.setWifiEnabledBlocking().
在setWifiEnabledBlocking()函數(shù)中主要做如下工作:加載Wifi驅(qū)動,啟動wpa_supplicant,注冊廣播接收器,啟動WifiThread監(jiān)聽線程。代碼如下:
……
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使能結(jié)束,自動進入掃描階段。
(2) 掃描AP
當(dāng)驅(qū)動加載成功后,如果配置文件的AP_SCAN = 1,掃描會自動開始,WifiMonitor將會從supplicant收到一個消息EVENT_DRIVER_STATE_CHANGED,調(diào)用 handleDriverEvent(),然后調(diào)用mWifiStateTracker.notifyDriverStarted(),該函數(shù)向消息隊列 添加EVENT_DRIVER_STATE_CHANGED,handlermessage()函數(shù)處理消息時調(diào)用scan()函數(shù),并通過 WifiNative將掃描命令發(fā)送到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的掃描,用戶當(dāng)然也可以手動掃描AP,這部分實現(xiàn)在WifiService里面,WifiService通過startScan()接口函數(shù)發(fā)送掃描命令到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);
}
然后下面的流程同上面的自動掃描,我們來分析一下手動掃描從哪里開始的。我們應(yīng)該知道手動掃描是通過菜單鍵的掃描鍵來響應(yīng)的,而響應(yīng)該動作的應(yīng)該是 WifiSettings類中Scanner類的handlerMessage()函數(shù),它調(diào)用WifiManager的 startScanActive(),這才調(diào)用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);
}
當(dāng)按下菜單鍵時,WifiSettings就會調(diào)用這個函數(shù)繪制菜單。如果選擇掃描按鈕,WifiSettings會調(diào)用onOptionsItemSelected()。
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java[!--empirenews.page--]
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()就會調(diào)用WifiService里的startScan()函數(shù),下面的流程和上面的一樣,這里不贅述。
當(dāng)supplicant完成了這個掃描命令后,它會發(fā)送一個消息給上層,提醒他們掃描已經(jīng)完成,WifiMonitor會接收到這消息,然后再發(fā)送給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消息,它會調(diào)用handleEvent(),調(diào)用流程如下所示。
WifiSettings.handleEvent() =>WifiSettings.updateAccessPoints() => mWifiManager.getScanResults() => mService.getScanResults()=> mWifiStateTracker.scanResults() => WifiNative.scanResultsCommand()……
將獲取AP列表的命令發(fā)送到supplicant,然后supplicant通過Socket發(fā)送掃描結(jié)果,由上層接收并顯示。這和前面的消息獲取流程基本相同。
(3)配置,連接AP
當(dāng)用戶選擇一個活躍的AP時,WifiSettings響應(yīng)打開一個對話框來配置AP,比如加密方法和連接AP的驗證模式。配置好AP后,WifiService添加或更新網(wǎng)絡(luò)連接到特定的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;
}
配置好以后,當(dāng)按下“Connect Press”時,WifiSettings通過發(fā)送LIST_NETWORK命令到supplicant來檢查該網(wǎng)絡(luò)是否配置。如果沒有該網(wǎng)絡(luò)或沒有配置 它,WifiService調(diào)用addorUpdateNetwork()函數(shù)來添加或更新網(wǎng)絡(luò),然后發(fā)送命令給supplicant,連接到這個網(wǎng)絡(luò)。 下面是從響應(yīng)連接按鈕到WifiService發(fā)送連接命令的代碼:
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) {[!--empirenews.page--]
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()通過調(diào)用mWifiStateTracker.setNetworkVariable()將連接命令發(fā)送到Wpa_supplicant。
(4)獲取IP地址
當(dāng)連接到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發(fā)送EVENT_SUPPLICANT_CONNECTION消息到消息隊列,這個消息有自己的handlermessage()函數(shù)處理,它會啟動一個DHCP線程,而這個線程會一直等待一個消息事件,來啟動DHCP協(xié)議分配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);
……
……
}
當(dāng)Wpa_supplicant連接到AP后,它會發(fā)送一個消息給上層來通知連接成功,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線程動態(tài)IP
if(!mHaveIpAddress && !mObtainingIpAddress) {
mObtainingIpAddress = true;
//發(fā)送啟動DHCP線程獲取IP
mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
}
} else { //使用靜態(tài)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); //發(fā)送IP獲得成功消息事件
}
}
DhcpThread獲取EVENT_DHCP_START消息事件后,調(diào)用handleMessage()函數(shù),啟動DHCP獲取IP地址的服務(wù)。[!--empirenews.page--]
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());
}
……
}
這 里調(diào)用了一個NetworkUtils.runDhcp()函數(shù),NetworkUtils類是一個網(wǎng)絡(luò)服務(wù)的輔助類,它主要定義了一些本地接口,這些接 口會通過他們的JNI層android_net_NetUtils.cpp文件和DHCP client通信,并獲取IP地址。
至此,IP地址獲取完畢,Wifi啟動流程結(jié)束。