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的基本架構
1、wifi使用者空間的程式和庫:
external/wpa_supplicant/
生成庫libwpaclient.so和守護程序wpa_supplicant
2、hardware/libhardware_legary/wifi/是wifi管理庫
3、JNI部分:
frameworks/base/core/jni/android_net_wifi_Wifi.cpp
4、JAVA部分:
frameworks/base/services/java/com/android/server/
frameworks/base/wifi/java/android/net/wifi/
5、WIFI Settings應用程式位於:
packages/apps/Settings/src/com/android/settings/wifi/
6、WIFI 驅動模組 wlan.ko
wpa_supplicant通過wireless_ext介面和驅動通訊
7、WIFI 硬體模組
2、WIFI在Android中如何工作
Android使用一個修改版wpa_supplicant作為daemon來控制WIFI,程式碼位於external/wpa_supplicant。wpa_supplicant是通過socket與hardware/libhardware_legacy/wifi/wifi.c通訊。UI通過android.net.wifipackage(frameworks/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_SUPPLICANT為true,預設使用驅動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 binary,WIFI需要的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接受來自底層的事件,WifiService和WifiMonitor是整個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(),這個函式會呼叫WifiManager的setWifiEnabled()函式。
[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只是個服務代理,所以它會呼叫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監聽執行緒。程式碼如下:
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_CHANGED,handlermessage()函式處理訊息時呼叫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(),這才呼叫WifiService的startScan()。
如程式碼: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(0, 6000);
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