天天看點

andorid wifi 子產品分析

一.啟動wifi服務

1.在 SystemServer 啟動的時候,會生成一個 ConnectivityService 的執行個體

路徑為:\frameworks\base\services\java\com\android\server\SystemServer.java

try {

             Slog.i(TAG, "Connectivity Service");

             connectivity = ConnectivityService.getInstance(context);

             ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);

            } catch (Throwable e) {

                Slog.e(TAG, "Failure starting Connectivity Service", e);

            }

2.ConnectivityService 類中private ConnectivityService(Context context) 構造函數建立

    WifiService和WifiStateTracker對象

\frameworks\base\services\java\com\android\server\ConnectivityService.java

for (int netType : mPriorityList) {

            switch (mNetAttributes[netType].mRadio) {

            case ConnectivityManager.TYPE_WIFI:

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

                WifiStateTracker wst = new WifiStateTracker(context, mHandler);

                WifiService wifiService = new WifiService(context, wst);

                ServiceManager.addService(Context.WIFI_SERVICE, wifiService);

                wifiService.startWifi();

                mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;

                wst.startMonitoring();

                break;

}

}

3.建立WifiStateTracker和WifiService對象用來啟動wifi管理服務WifiStateTracker 會建立   

    WifiMonitor 用來接收來自底層的事件,WifiService 和 WifiMonitor 是整個子產品的核心。

4.WifiService 負責啟動關閉 wpa_supplicant、啟動關閉 WifiMonitor 監視線程和把指令下發 

               給 wpa_supplicant

     5. WifiMonitor

               開始運作線程,會請求連接配接wpa_supplicant,通過調用WifiStateTracker函數connectToSupplicant然後通過wifi.c的wifi_connect_to_supplicant,接着向wpa_ctrl的wpa_ctrl_open。然後通過CreateFile函數向wpa_supplicant讀取資料。連接配接成功後會發送EVENT_SUPPLICANT_CONNECTION消息啟動擷取DHCP位址線程阻塞調用(當連接配接上AP的時候,該線程會被執行起來擷取IP位址),并記錄自己的MAC位址(因為MAC位址不會改變

是以請求一次即可) 接着開啟一個死循環處理wpa_supplicant發送的事件。

當使用者點選Wi-Fi按鈕 的時候WifiEnabler中的onPreferenceChange函數會被調用,再由該函數調用WifiManager的setWifiEnabled函數,它先引用AIDL經由IWifiManager通過Binder機制調用WifiService的setWifiEnabled設定Wifi開啟狀态。同時WifiService會發送MESSAGE_ENABLE_WIFI消息,由WifiService的 

setWifiEnabledBlocking函數響應該消息,負責Wifi可用的需要工作。首先他會加載驅動

loadDriver(),然後開啟wpa_supplicant( 配 置 文 件 硬 編 碼 為

"/data/misc/wifi/wpa_supplicant.conf") 再注冊廣播消息,而後通過 WifiStateTracker 來啟動 WifiMonitor 中的監視線程。以上工作使能成功後,會調用setWifiEnabledState最後廣播WIFI_STATE_CHANGED_ACTION 這個Intent,至此Wifi能動開啟。

接下來是掃描AP。

WifiSettings和WifiEnabler 創 建 的 時 候 就 會 向 Android 注 冊 接 收

WIFI_STATE_CHANGED_ACTION,是以他們都會收到WIFI_STATE_CHANGED_ACTION 這個Intent,WifiEnabler負責使得圖示加亮,WifiSettings負責使得開啟掃描AP。經由

WifiService 的 startScan,再通過JNI由android_net_wifi_scanCommand函數向wpa_supplicant發送掃描指令.當wpa_supplicant 處理完 SCAN 指令後,它會向控制通道發送事件通知掃描完成,進而wifi_wait_for_event 函數會接收到該事件,由此WifiMonitor 中的 MonitorThread 會被執行來處理接掃描結果事件。此線程通過WifiStateTracker 廣播SCAN_RESULTS_AVAILABLE_ACTION這個Intent。而WifiSettings注冊了接收此Intent,最終由其相應函數updateAccessPoints将AP清單,以GUI的形式列出來。

  Wifi 連接配接部分

當使用者選擇一個AP時會彈出一個AP參數配置對話框,此對話框會顯示目前選擇的AP信号強度,若此AP設定了密碼則需要使用者輸入密碼才能登入。WifiSettings中的 onPreferenceTreeClick會被調用          @Override

    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {

         //點選AP響應函數

        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;

    }

使用者配置好之後點選連接配接按鈕,onClick函數會被調用。

public void onClick(DialogInterface dialogInterface, 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);

                    }

                }

            }

        }

連接配接請求部分

一.Settings的connect函數響應連接配接,更新網絡儲存配置,更新設定目前選擇的優先級最高,并

    儲存。然後通過enableNetwork使得其他網絡不可用來進行連接配接。最後調用WifiManager的

      reconnect函數連接配接目前選擇的網絡。

二.WifiManager的reconnect函數通過AIDL的Binder機制,調用WifiService的reconnect函數

三.然後會調用 WifiStateTracker的reconnectCommand函數,通過JNI(android_net_wifi_Wifi)的             

      android_net_wifi_reconnectCommand 函數向WPA_WPASUPPLICANT發送 RECONNECT指令。

四. android_net_wifi_Wifi通過 doCommand(指令名,響應緩沖,響應緩存大小)調用wifi.c中的

      wifi_command函數來發送指令。

五.最後通過 wpa_ctrl的wpa_ctrl_request函數向控制通道發送連接配接指令。

傳回請求部分

六.當連接配接上之後WPA_SUPPLICANT會向控制通道發送連接配接成功指令。wifi.c的

      wifi_wait_for_event函數阻塞調用并傳回這個指令的字元串(CONNECTED).

七.而後WifiMonitor會被執行來處理這個事件,WifiMonitor 再調用 WifiStateTracker的

     notifyStateChange,WifiStateTracker 則接着會往自身發送 EVENT_DHCP_START 消息來啟動

    DHCP 去擷取 IP 位址,然後廣播NETWORK_STATE_CHANGED_ACTION消息,最後由

    WifiSettings類來響應,改變狀态和界面資訊。

關鍵函數功能介紹

一.connect函數功能

  1.updateNetwork:updateNetwork(config)會将目前選擇連接配接的AP配置資訊

   資訊傳遞進去,配置資訊有(網絡ID等)。如果網絡ID為-1則重新添加網絡配置,然後向

   wpa_supplicant 發送SET_NETWORK指令(即通過這個網絡ID設定其他一些相關資訊,設定

   SSID,密碼等)如果網絡配置不為-1則直接執行後面步驟即發送SET_NETWORK指令。

  2.saveNetwork:告訴supplicant儲存目前網絡配置并更新清單。SaveNetwork會調用WifiService的

     saveConfiguration向wpa_supplicant發送SAVE_CONFIG指令儲存目前網絡配置資訊,

    如果傳回false,則向wpa_supplicant重新發送RECONFIGURE指令擷取配置資訊,如果擷取信

    息成功後,會Intent一個  NETWORK_IDS_CHANGED_ACTION事件WifiSettings會注冊接受

    這個 時間并更新清單。

  3.enableNetwork函數,向系統擷取接口名并使得該接口有效。由于之前傳遞的disableOthers

    為true則向wpa_supplicant發送SELECT_NETWORK(如果傳遞的為false則發送                

     ENABLE_NETWORK指令),

  4.reconnect函數:連接配接AP

二.reconnect函數功能:connect函數會調用WifiManager的reconnect然後通過Binder機制調用

   WifiService的reconnect,再由WifiStateTracke調用WifiNative向wpa_supplicant發送

   RECONNECT指令去連接配接網絡,當連接配接上wpa_supplicant之後會向控制通道發送連接配接成功的命  

   令,

   wifi_wait_for_event函數阻塞等待該事件的發生,并傳回這個指令的字元串(CONNECTED)

三.android_net_wifi_Wifi函數的doCommand函數會調用wifi.c的wifi_command函數将上層的命

   令向wpa_supplicant發送。

四.wifi_wait_for_event函數以阻塞的方式,等待控制通道傳遞的事件。當有事件傳遞過來的時候

   該函數會通過wpa_ctrl的wpa_ctrl_recv函數讀取該事件,并以字元串形式傳回該事件名。

   int wifi_wait_for_event(char *buf, size_t buflen)

{

                       .......

    result = wpa_ctrl_recv(monitor_conn, buf, &nread);

    if (result < 0) {

        LOGD("wpa_ctrl_recv failed: %s\n", strerror(errno));

        strncpy(buf, WPA_EVENT_TERMINATING " - recv error", buflen-1);

        buf[buflen-1] = '\0';

        return strlen(buf);

    }

    buf[nread] = '\0';

    if (result == 0 && nread == 0) {

        LOGD("Received EOF on supplicant socket\n");

        strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1);

        buf[buflen-1] = '\0';

        return strlen(buf);

    }

    if (buf[0] == '<') {

        char *match = strchr(buf, '>');

        if (match != NULL) {

            nread -= (match+1-buf);

            memmove(buf, match+1, nread+1);

        }

    }

    return nread;

}

五.wpa_ctrl_request,通過socket方式向wpa_supplicant發送指令,以select模式阻塞在

   wpa_supplicant發送和接收。

int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,char *reply, size_t *reply_len,void (*msg_cb)(char *msg, size_t len))

{

         .......

                   res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);

                   if (FD_ISSET(ctrl->s, &rfds)) {

                            res = recv(ctrl->s, reply, *reply_len, 0);

                            if (res < 0)

                                     return res;

                            if (res > 0 && reply[0] == '<') {

                                     if (msg_cb) {

                                               if ((size_t) res == *reply_len)

                                                        res = (*reply_len) - 1;

                                               reply[res] = '\0';

                                               msg_cb(reply, res);

                                     }

                                     continue;

                            }

                            *reply_len = res;

                            break;

                   } else {

                            return -2;

                   }

         }

         return 0;

}

六.WifiMonitor 維護一個監視線程分發處理底層傳回上來的事件

 void handleEvent(int event, String remainder) {

            switch (event) {

                case DISCONNECTED:

                    handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);

                    break;

                case CONNECTED:

                    handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);

                    break;

                case SCAN_RESULTS:

                    mWifiStateTracker.notifyScanResultsAvailable();

                    break;

                case UNKNOWN:

                    break;

            }

        }

此時傳回的事件是CONNECTED是以 handleNetworkStateChange會被調用,驗證一下BSSID,重新獲得networkId

,然後調用WifiStateTracke的notifyStateChange通知狀态改變了的消息(EVENT_NETWORK_STATE_CHANGED)

接着處理這個消息,會移除可用網絡通告,然後通過 configureInterface()的動态擷取IP位址。最後

發送一個NETWORK_STATE_CHANGED_ACTION Intent,WifiSetings注冊了此Intent是以會響應該它。由updateConnectionState函數響應。

七.updateConnectionState 擷取連接配接資訊,更新清單狀态,設定為Connected,然後設定目前網絡為可用狀态

   private void updateConnectionState(DetailedState state) {

        if (!mWifiManager.isWifiEnabled()) {

            mScanner.pause();

            return;

        }

        if (state == DetailedState.OBTAINING_IPADDR) {

            mScanner.pause();

        } else {

            mScanner.resume();

        }

        mLastInfo = mWifiManager.getConnectionInfo();

        if (state != null) {

            mLastState = state;

        }

        for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {

            ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState);

        }

        if (mResetNetworks && (state == DetailedState.CONNECTED ||

                state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) {

            updateAccessPoints();

            enableNetworks();

        }

    }

 流程圖對應的源代碼路徑為:

 WifiEnabler,WifiSettings對應的路徑如下:

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

WifiManager,WifiMonitor,WifiStateTracker,WifiNative.對應的源代碼路徑如下:

froyo\frameworrks\base\wifi\java\android\net\wifi\

WifiService 對應代碼的位置

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

android_net_wifi_Wifi源代碼路徑如下:

froyo\frameworks\base\core\jni\

wifi_command,wifi_wait_for_envent源代碼路徑如下:

\hardware\libhardware_legacy\wifi\wifi.c

wpa_ctrl_源代碼路徑如下:

\external\wpa_supplicant\wpa_ctrl.c

wpa_supplicant源代碼路徑如下:

froyo\external\wpa_supplicant\

繼續閱讀