1. 程式人生 > >Android之wifi工作流程

Android之wifi工作流程

Android Wifi的工作流程

一、WIFI工作相關部分

Wifi 網絡卡狀態

1.    WIFI_STATE_DISABLED:WIFI網絡卡不可用

2.    WIFI_STATE_DISABLING:WIFI正在關閉

3.    WIFI_STATE_ENABLED:WIFI網絡卡可用

4.    WIFI_STATE_ENABLING:WIFI網絡卡正在開啟

5.    WIFI_STATE_UNKNOWN:未知網絡卡狀態

 

WIFI 訪問網路需要的許可權

"android.permission.CHANGE_NETWORK_STATE">

修改網路狀態的許可權

 

android:name="android.permission.CHANGE_WIFI_STATE">

修改WIFI狀態的許可權

"android.permission.ACCESS_NETWORK_STATE">

訪問網路許可權

"android.permission.ACCESS_WIFI_STATE">

訪問WIFI許可權

 

WIFI 核心模組

n  WifiService

由SystemServer啟動的時候生成的ConnecttivityService建立,負責啟動關閉wpa_supplicant,啟動和關閉WifiMonitor執行緒,把命令下發給wpa_supplicant以及跟新WIFI的狀態

n  WifiMonitor

  負責從wpa_supplicant接收事件通知

n  Wpa_supplicant

1、讀取配置檔案

2、初始化配置引數,驅動函式

3、讓驅動scan當前所有的bssid

4、檢查掃描的引數是否和使用者設定的想否

5、如果相符,通知驅動進行許可權 認證操作

6、連上AP

n  Wifi驅動模組

廠商提供的source,主要進行load firmware和kernel的wireless進行通訊

n  Wifi電源管理模組

主要控制硬體的GPIO和上下電,讓CPU和Wifi模組之間通過sdio介面通訊

 

二、Wifi工作步驟

1  Wifi模組初期化

2  Wifi啟動

3  查詢熱點(AP)

4 配置AP

5  配置AP引數

6  Wifi連線

7  IP地址配置

三、WIFI的架構和流程

1、WIFI的基本架構

   1wifi使用者空間的程式和庫:

         external/wpa_supplicant/

       生成庫libwpaclient.so和守護程序wpa_supplicant

   2hardware/libhardware_legary/wifi/wifi管理庫

   3JNI部分:

         frameworks/base/core/jni/android_net_wifi_Wifi.cpp

   4JAVA部分:

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

         frameworks/base/wifi/java/android/net/wifi/

   5WIFI Settings應用程式位於:

       packages/apps/Settings/src/com/android/settings/wifi/

   6WIFI 驅動模組 wlan.ko

        wpa_supplicant通過wireless_ext介面和驅動通訊

   7WIFI 硬體模組 

2、WIFI在Android中如何工作

       Android使用一個修改版wpa_supplicant作為daemon來控制WIFI,程式碼位於external/wpa_supplicantwpa_supplicant是通過sockethardware/libhardware_legacy/wifi/wifi.c通訊。UI通過android.net.wifipackageframeworks/base/wifi/java/android/net/wifi/)傳送命令給wifi.c。相應的JNI實現位於frameworks/base/core/jni/android_net_wifi_Wifi.cpp。更高一級的網路管理位於frameworks/base/core/java/android/net

3、配置Android支援WIFI

   BoardConfig.mk中新增:

      BOARD_HAVE_WIFI := true

      BOARD_WPA_SUPPLICANT_DRIVER :=WEXT

   這將在external/wpa_supplicant/Android.mk設定WPA_BUILD_SUPPLICANTtrue,預設使用驅動driver_wext.c

   如果使用定製的wpa_supplicant驅動(例如 wlan0),可以設定:

       BOARD_WPA_SUPPLICANT_DRIVER := wlan0

4、使能wpa_supplicant除錯資訊

   預設wpa_supplicant設定為MSG_INFO,為了輸出更多資訊,可修改:

   1、在common.c中設定wpa_debug_level= MSG_DEBUG;

   2、在common.c中把#definewpa_printf巨集中的

      if ((level) >= MSG_INFO)

      改為

      if ((level) >= MSG_DEBUG)

5、配置wpa_supplicant.conf

   wpa_supplicant是通過wpa_supplicant.conf中的ctrl_interface=來指定控制socket的,應該在AndroidBoard.mk中配置好複製到$(TARGET_OUT_ETC)/wifi(也就是/system/etc/wifi/wpa_supplicant.conf)這個位置會在init.rc中再次檢測的。

   一般的wpa_supplicant.conf配置為:

      ctrl_interface=DIR=/data/system/wpa_supplicantGROUP=wifi

      update_config=1

      fast_reauth=1  

   有時,驅動需要增加:

      ap_scan=1

   如果遇到AP連線問題,需要修改ap_scan=0來讓驅動連線,代替wpa_supplicant

   如果要連線到non-WPA or open wirelessnetworks,要增加:

      network={

              key_mgmt=NONE

      }

6、配置路徑和許可權

   Google修改的wpa_supplicant要執行在wifi使用者和組下的。程式碼可見wpa_supplicant/os_unix.c

   中的os_program_init()函式。

  

   如果配置不對,會出現下面錯誤:

      E/WifiHW  (  ): Unableto open connection to supplicant on

      "/data/system/wpa_supplicant/wlan0":No such file or directory will appear.

   確認init.rc中有如下配置:

       mkdir /system/etc/wifi 0770 wifi wifi

       chmod 0770 /system/etc/wifi

       chmod 0660/system/etc/wifi/wpa_supplicant.conf

       chown wifi wifi/system/etc/wifi/wpa_supplicant.conf

       # wpa_supplicant socket

       mkdir /data/system/wpa_supplicant 0771wifi wifi

       chmod 0771 /data/system/wpa_supplicant

       #wpa_supplicant control socket forandroid wifi.c

       mkdir /data/misc/wifi 0770 wifi wifi

       mkdir /data/misc/wifi/sockets 0770wifi wifi

       chmod 0770 /data/misc/wifi

       chmod 0660/data/misc/wifi/wpa_supplicant.conf

   如果系統的/system目錄為只讀,那應該使用路徑/data/misc/wifi/wpa_supplicant.conf

7、執行wpa_supplicant和dhcpcd

   init.rc中確保有如下語句:

      service wpa_supplicant/system/bin/logwrapper /system/bin/wpa_supplicant -dd

                   -Dwext -iwlan0 -c/data/misc/wifi/wpa_supplicant.conf

         user root

         group wifi inet

      socket wpa_wlan0 dgram 660 wifiwifi

         oneshot

      service dhcpcd/system/bin/logwrapper /system/bin/dhcpcd -d -B wlan0

         disabled

         oneshot

   根據所用的WIFI驅動名字,修改wlan0為自己驅動的名字。

8、編譯WIFI驅動為module或kernel built in

   1、編譯為module

      BoardConfig.mk中新增:

         WIFI_DRIVER_MODULE_PATH :="/system/lib/modules/xxxx.ko"

         WIFI_DRIVER_MODULE_ARG := "" #for example nohwcrypt

         WIFI_DRIVER_MODULE_NAME :="xxxx"  #for example wlan0 

         WIFI_FIRMWARE_LOADER := ""        

   2、編譯為kernel built in  

     1)在hardware/libhardware_legacy/wifi/wifi.c要修改interface名字,

     2)在init.rc中新增:

        setprop wifi.interface"wlan0"

     3)在hardware/libhardware_legacy/wifi/wifi.c中當insmod/rmmod時,

        直接return 0

9、WIFI需要的firmware

   Android不使用標準的hotplug binaryWIFI需要的firmware要複製到/etc/firmware

   或者複製到WIFI驅動指定的位置,然後WIFI驅動會自動載入。

10、修改WIFI驅動適合Android

   Google修改的wpa_supplicant要求SIOCSIWPRIVioctl傳送命令到驅動,及接收資訊,例如signalstrength, mac address of the AP, link speed等。所以要正確實現WIFI驅動,需要從SIOCSIWPRIVioctl返回RSSI(signal strength)MACADDR資訊。

 

   如果沒實現這個ioctl,會出現如下錯誤:

     E/wpa_supplicant(  ):wpa_driver_priv_driver_cmd failed

                               wpa_driver_priv_driver_cmd RSSI len = 4096 

     E/wpa_supplicant(  ):wpa_driver_priv_driver_cmd failed 

     D/wpa_supplicant(  ):wpa_driver_priv_driver_cmd LINKSPEED len = 4096

     E/wpa_supplicant(  ):wpa_driver_priv_driver_cmd failed 

     I/wpa_supplicant(  ):CTRL-EVENT-DRIVER-STATE HANGED

10、設定dhcpcd.conf

   一般/system/etc/dhcpcd/dhcpcd.conf的配置為:

      interface wlan0

      option subnet_mask, routers,domain_name_server

四、WIFI模組分析

Wifi模組學習流程

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

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

a    Wifi的啟動流程(有程式碼供參考分析)

b    Wifi模組相關檔案的解析

c    Wpa_supplicant解析

Awifi的基本執行流程(針對程式碼而言)

首先給一張我網上down下來的圖,針對2.3版本之前的,由於不怎麼擅長畫這些,大家也就將就點,只要能助理解就可以了

(一)初始化

a.流程

1.SystemServer啟動的時候會生成一個ConnectivityService的例項

2.ConnectivityService的建構函式會建立WifiService

3.WifiStateTracker會建立WifiMonitor接受來自底層的事件,WifiServiceWifiMonitor是整個wifi模組的核心,WifiService負責啟動和關閉wpa_supplicant,啟動和關閉WifiMonitor監視執行緒和把命令下方給wpa_supplicant,而WifiMonitor則負責從wpa_supplicant接受事件通知

b.程式碼分析

要想使用Wifi模組,必須首先使能Wifi,當你第一次按下Wifi使能按鈕時,WirelessSettings會例項化一個WifiEnabler物件,例項化程式碼如下:

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

[java] view plaincopy

1.  protected void onCreate(Bundle savedInstanceState) {  

2.    

3.         super.onCreate(savedInstanceState);  

4.  ……  

5.                CheckBoxPreferencewifi = (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI);  

6.    

7.                mWifiEnabler= new WifiEnabler(this, wifi);  

8.  ……  

9.  }  

WifiEnabler類的定義大致如下,它實現了一個監聽介面,當WifiEnabler物件被初始化後,它監聽到你按鍵的動作,會呼叫響應函式 onPreferenceChange(),這個函式會呼叫WifiManagersetWifiEnabled()函式。

[java] view plaincopy

1.  public class WifiEnabler implementsPreference.OnPreferenceChangeListener {  

2.  ……  

3.  public boolean onPreferenceChange(Preference preference,Object value) { 

4.          booleanenable = (Boolean) value;   

5.  ……  

6.  if (mWifiManager.setWifiEnabled(enable)) {  

7.                  mCheckBox.setEnabled(false);  

8.  ……  

9.  }  

10. ……  

11. }  

我們都知道Wifimanager只是個服務代理,所以它會呼叫WifiServicesetWifiEnabled()函式,而這個函式會呼叫 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監聽執行緒。程式碼如下:

1.  ……  

2.  if (enable) { 

3.             if (!mWifiStateTracker.loadDriver()) {  

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

5.                 setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);  

6.                  return false;  

7.             } 

8.             if (!mWifiStateTracker.startSupplicant()) { 

9.                  mWifiStateTracker.unloadDriver(); 

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

11.                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);  

12.                 return false;  

13.            } 

14.            registerForBroadcasts(); 

15.            mWifiStateTracker.startEventLoop(); 

16. ……  

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

(二)掃描AP

當驅動載入成功後,如果配置檔案的AP_SCAN = 1,掃描會自動開始,WifiMonitor將會從supplicant收到一個訊息EVENT_DRIVER_STATE_CHANGED,呼叫handleDriverEvent(),然後呼叫mWifiStateTracker.notifyDriverStarted(),該函式向訊息佇列新增EVENT_DRIVER_STATE_CHANGEDhandlermessage()函式處理訊息時呼叫scan()函式,並通過 WifiNative將掃描命令傳送到wpa_supplicant

看程式碼Frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java

[java] view plaincopy

1.  private void handleDriverEvent(Stringstate) { 

2.             if (state == null) { 

3.                  return;  

4.             } 

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

6.                 mWifiStateTracker.notifyDriverStopped(); 

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

8.                  mWifiStateTracker.notifyDriverStarted(); 

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

10.                 mWifiStateTracker.notifyDriverHung(); 

11.            } 

12. }  

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

[java] view plaincopy

1.  ...  

2.  case EVENT_DRIVER_STATE_CHANGED:  

3.                  switch(msg.arg1) {  

4.                  case DRIVER_STARTED:  

5.                        

6.                      setNumAllowedChannels(); 

7.                     synchronized (this) {  

8.                         if (mRunState == RUN_STATE_STARTING) { 

9.                             mRunState = RUN_STATE_RUNNING; 

10.                            if (!mIsScanOnly) {  

11.                                 reconnectCommand(); 

12.                            } else {  

13.                                 // In somesituations, supplicant needs to be kickstarted to  

14.                                 // start thebackground scanning  

15.                                 scan(true);  

16.                            } 

17.                        } 

18.                     } 

19.                    break;               

20. ...  

上面是啟動Wifi時,自動進行的AP的掃描,使用者當然也可以手動掃描AP,這部分實現在WifiService裡面,WifiService通過startScan()介面函式傳送掃描命令到supplicant

看程式碼:Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

[java] view plaincopy

1.  public boolean startScan(booleanforceActive) { 

2.         enforceChangePermission(); 

3.         switch (mWifiStateTracker.getSupplicantState()) { 

4.             case DISCONNECTED:  

5.             case INACTIVE:   

6.             case SCANNING:  

7.             case DORMANT: 

8.                  break;  

9.             default:  

10.                mWifiStateTracker.setScanResultHandling( 

11.                        WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); 

12.                 break;  

13.        } 

14.        return mWifiStateTracker.scan(forceActive);  

15. }  

然後下面的流程同上面的自動掃描,我們來分析一下手動掃描從哪裡開始的。我們應該知道手動掃描是通過選單鍵的掃描鍵來響應的,而響應該動作的應該是 WifiSettings類中Scanner類的handlerMessage()函式,它呼叫WifiManager startScanActive(),這才呼叫WifiServicestartScan()

如程式碼:packages/apps/Settings/src/com/android/settings/wifiwifisettings.java

[java] view plaincopy

1.  public boolean onCreateOptionsMenu(Menu menu) { 

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

3.                 .setIcon(R.drawable.ic_menu_scan_network); 

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

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

6.         return super.onCreateOptionsMenu(menu);  

7.  }  

當按下選單鍵時,WifiSettings就會呼叫這個函式繪製選單。如果選擇掃描按鈕,WifiSettings會呼叫onOptionsItemSelected()

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

1.  public booleanonOptionsItemSelected(MenuItem item) { 

2.         switch (item.getItemId()) {  

3.             case MENU_ID_SCAN:  

4.                  if(mWifiManager.isWifiEnabled()) {  

5.                      mScanner.resume(); 

6.                  } 

7.                  return true;  

8.             case MENU_ID_ADVANCED:  

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

10.                 return true;  

11.        } 

12.        return super.onOptionsItemSelected(item);  

13. }  

Handler類:

1.  private class Scanner extends Handler {   

2.         private int mRetry = 0;   

3.         void resume() {  

4.             if (!hasMessages(0)) {   

5.                  sendEmptyMessage(0);  

6.             }  

7.         } 

8.         void pause() {  

9.             mRetry = 0;  

10.             mAccessPoints.setProgress(false);  

11.            removeMessages(0);  

12.        } 

13.        @Override  

14.        public void handleMessage(Message message) { 

15.            if (mWifiManager.startScanActive()){  

16.                 mRetry = 0;  

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

18.                 mRetry = 0;  

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

20.                        Toast.LENGTH_LONG).show(); 

21.                 return;  

22.            } 

23.            mAccessPoints.setProgress(mRetry != 0);  

24.            sendEmptyMessageDelayed(06000);  

25.        } 

26. }  

這裡的mWifiManager.startScanActive()就會呼叫WifiService裡的startScan()函式,下面的流程和上面的一樣,這裡不贅述。

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

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

1.  void handleEvent(int event, String remainder) {  

2.              switch (event) {  

3.                  caseDISCONNECTED: 

4.                     handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED,remainder); 

5.                      break;  

6.                  case CONNECTED:  

7.                     handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,remainder); 

8.                      break;  

9.                  case SCAN_RESULTS:  

10.                     mWifiStateTracker.notifyScanResultsAvailable(); 

11.                     break;  

12.                 case UNKNOWN:  

13.                     break;  

14.             } 

15. }  

WifiStateTracker將會廣播SCAN_RESULTS_AVAILABLE_ACTION訊息:

程式碼如:Frameworks/base/wifi/java/android/net/wifi/WifiStateTracker.java

1.  public voidhandleMessage(Message msg) {  

2.          Intent intent;  

3.  ……  

4.  case EVENT_SCAN_RESULTS_AVAILABLE:  

5.                  if(ActivityManagerNative.isSystemReady()) {  

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

7.                  }  

8.                  sendScanResultsAvailable();  

9.                    

10.                 setScanMode(false); 

11.                 break;  

12. ……  

13.   

由於WifiSettings類註冊了intent,能夠處理SCAN_RESULTS_AVAILABLE_ACTION訊息,它會呼叫handleEvent(),呼叫流程如下所示。

WifiSettings.handleEvent() ====>WifiSettings.updateAccessPoints() ====> mWifiManager.getScanResults()====> mService.getScanResults()====>mWifiStateTracker.scanResults() ====> WifiNative.scanResultsCommand()……
將獲取AP列表的命令傳送到supplicant,然後supplicant通過Socket傳送掃描結果,由上層接收並顯示。這和前面的訊息獲取流程基本相同。

(三)配置,連線AP

當用戶選擇一個活躍的AP時,WifiSettings響應開啟一個對話方塊來配置AP,比如加密方法和連線AP的驗證模式。配置好AP後,WifiService新增或更新網路連線到特定的AP
程式碼如:packages/apps/settings/src/com/android/settings/wifi/WifiSetttings.java

1.  public booleanonPreferenceTreeClick(PreferenceScreen screen, Preference preference) {   

2.         if (preference instanceo