settings裏的 wifi流程

 Wifi啓動流程

       (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,Object value) {

        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()) {

           case DISCONNECTED:

           case INACTIVE:

           case SCANNING:

           case DORMANT:

                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);

       return super.onCreateOptionsMenu(menu);

    }

 

       當按下菜單鍵時,WifiSettings就會調用這個函數繪製菜單。如果選擇掃描按鈕,WifiSettings會調用onOptionsItemSelected()。

 

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

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()就會調用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(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消息,它會調用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 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;

    }

 

       配置好以後,當按下“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) {

           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);

                    }

                }

           }

       }

    }

 

 

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()通過調用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

                // to the 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) {

           case EVENT_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) {

               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線程動態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) {

               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());

        }

……

}

這裏調用了一個NetworkUtils.runDhcp()函數,NetworkUtils類是一個網絡服務的輔助類,它主要定義了一些本地接口,這些接口會通過他們的JNI層android_net_NetUtils.cpp文件和DHCP client通信,並獲取IP地址。

至此,IP地址獲取完畢,Wifi啓動流程結束。

發佈了70 篇原創文章 · 獲贊 6 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章