<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    wifi詳解(二)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    1        Wifi模塊解析和啟動流程

    1.1      框架分析

    WIFI整體框架如圖所示:

     

    首先,用戶程序使用WifiManager類來管理Wifi模塊,它能夠獲得Wifi模塊的狀態,配置和控制Wifi模塊,而所有這些操作都要依賴 Wifiservice類來實現。

     WifiService和WifiMonitor類是Wifi框架的核心,如圖所示。下面先來看看WifiService是什么時候,怎么被創建和初始化 的。

     在systemServer啟動之后,它會創建一個 ConnectivityServer對象,這個對象的構造函數會創建一個WifiService的實例,代碼如下所示:

     

    framework/base/services/java/com/android/server/ConnectivityService.java

    {

    ……

    case ConnectivityManager.TYPE_WIFI:

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

                   WifiStateTrackerwst = new WifiStateTracker(context,mHandler);                            //創建WifiStateTracker實例

                   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.2      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,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啟動流程結束。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全