WIFI總體框架如圖所示: java
首先,用戶程序使用WifiManager類來管理Wifi模塊,它可以得到Wifi模塊的狀態,配置和控制Wifi模塊,而全部這些操做都要依賴 Wifiservice類來實現。 android
WifiService和WifiMonitor類是Wifi框架的核心,如圖所示。下面先來看看WifiService是何時,怎麼被建立和初始化 的。 網絡
在systemServer啓動以後,它會建立一個 ConnectivityServer對象,這個對象的構造函數會建立一個WifiService的實例,代碼以下所示: app
framework/base/services/java/com/android/server/ConnectivityService.java 框架
{ less
…… ide
case ConnectivityManager.TYPE_WIFI: 函數
if(DBG) Slog.v(TAG, "Starting Wifi Service."); oop
WifiStateTrackerwst = new WifiStateTracker(context,mHandler); //建立WifiStateTracker實例 ui
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);
(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,Objectvalue) {
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()) {
caseDISCONNECTED:
caseINACTIVE:
caseSCANNING:
caseDORMANT:
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);
returnsuper.onCreateOptionsMenu(menu);
}
當按下菜單鍵時,WifiSettings就會調用這個函數繪製菜單。若是選擇掃描按鈕,WifiSettings會調用 onOptionsItemSelected()。
packages/apps/Settings/src/com/android/settings/wifiwifisettings.java
public booleanonOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
caseMENU_ID_SCAN:
if(mWifiManager.isWifiEnabled()) {
mScanner.resume();
}
return true;
caseMENU_ID_ADVANCED:
startActivity(new Intent(this,AdvancedSettings.class));
return true;
}
returnsuper.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 voidhandleMessage(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(newIntent(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 instanceofAccessPoint) {
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 {
returnsuper.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) {
WifiConfigurationconfig = 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 {
intnetworkId =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;
}
returnaddOrUpdateNetwork(config);
}
private intaddOrUpdateNetwork(WifiConfiguration config) {
try {
return mService.addOrUpdateNetwork(config);
} catch (RemoteExceptione) {
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
// tothe 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) {
caseEVENT_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) {
caseDISCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder);
break;
caseCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder);
break;
……
}
private void handleNetworkStateChange(NetworkInfo.DetailedStatenewState, String data) {
StringBSSID = null;
intnetworkId = -1;
if(newState ==NetworkInfo.DetailedState.CONNECTED) {
Matchermatch = mConnectedEventPattern.matcher(data);
if(!match.find()) {
if(Config.LOGD) Log.d(TAG, "Could not find BSSID in CONNECTEDeventstring");
}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, intnetworkId) {
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) {
caseEVENT_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啓動流程結束。