<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                本節將圍繞setWifiEnabled、startScanActive和connect函數來介紹WifiService的工作流程。先來看setWifiEnabled函數。 **1、setWifiEnabled函數分析** WifiService的setWifiEnabled函數將會調用WifiStateMachine的setWifiEnabled,故此處直接來看。 **WifiStateMachine.java::setWifiEnabled** ~~~ public void setWifiEnabled(boolean enable) { mLastEnableUid.set(Binder.getCallingUid()); if (enable) {// 發送兩條消息 sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0)); sendMessage(CMD_START_SUPPLICANT); } else { sendMessage(CMD_STOP_SUPPLICANT); sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0)); } } ~~~ 其中,CMD_LOAD_DRIVER和CMD_START_SUPPLICANT消息將交由WifiStateMachine來處理。由于WifiStateMachine此時還處于DriverUnloaded狀態,DriverUnloaded的函數processMessage將被調用。 **①、CMD_LOAD_DRIVER處理流程** 先來看它對CMD_LOAD_DRIVER的處理,相關代碼如下所示。 **WifiStateMachine.java::DriverUnloaded:processMessage** ~~~ public boolean processMessage(Message message) { switch (message.what) { case CMD_LOAD_DRIVER: transitionTo(mDriverLoadingState);// 轉到DriverLoadingState break; default: return NOT_HANDLED; } return HANDLED; } ~~~ >[info] 提示 由于篇幅原因,本章不討論狀態切換過程中所涉及的各狀態的exit函數。 先執行DriverLoadingState的enter函數,代碼如下所示。 **WifiStateMachine.java::DriverLoadingState:enter** ~~~ class DriverLoadingState extends State { public void enter() { final Message message = new Message(); message.copyFrom(getCurrentMessage()); // 復制當前消息,即上面的CMD_LOAD_DRIVER消息 new Thread(new Runnable() {// 單獨啟動一個線程來加載wlan驅動 public void run() { mWakeLock.acquire(); switch(message.arg1) { case WIFI_STATE_ENABLING:// CMD_LOAD_DRIVER攜帶了此信息 // 該函數內部將發送WIFI_STATE_CHANGED_ACTION廣播 setWifiState(WIFI_STATE_ENABLING); break; ...... } // 加載wlan驅動,如果成功則發送CMD_LOAD_DRIVER_SUCCESS消息 if(mWifiNative.loadDriver()) sendMessage(CMD_LOAD_DRIVER_SUCCESS); else ......// 失敗的處理 mWakeLock.release(); } }).start(); } } ~~~ 由上述代碼可知CMD_LOAD_DRIVER消息的處理流程如下。 * DriverUnloaded狀態直接切換到DriverLoading狀態。 * DriverLoading的enter函數中將創建一個工作線程來加載wlan driver。如果成功,它將發送CMD_LOAD_DRIVER_SUCCESS消息。 WifiNative的loadDriver將借助JNI調用以觸發wifi.c中的wifi_load_driver函數被調用,其代碼如下所示。 **Wifi.c::wifi_load_driver** ~~~ int wifi_load_driver() { /* 該宏定義了wlan driver的文件路徑名。在AOSP代碼中,沒有地方定義該宏。不過Galaxy Note2 對應的driver文件路徑是“/lib/modules/dhd.ko”。 */ #ifdef WIFI_DRIVER_MODULE_PATH char driver_status[PROPERTY_VALUE_MAX]; int count = 100; if (is_wifi_driver_loaded()) return 0; /* DRIVER_MODULE_PATH變量保存了WIFI_DRIVER_MODULE_PATH宏定義的文件路徑名。 如果上面那個宏定義了,此處將通過insmod向內核添加wlan driver。 */ if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0) return -1; /* FIRMWARE_LOADER變量指向WIFI_FIRMWARE_LOADER宏定義的wlan固件加載程序文件路徑名 DRIVER_PROP_NAME的值為“wlan.driver.status”。如果沒有指定wlan固件加載程序, 則直接設置“wlan.driver.status”屬性值為“ok”,否則通過“ctrl.start”方式來啟動wlan 固件加載程序。 */ if (strcmp(FIRMWARE_LOADER,"") == 0) property_set(DRIVER_PROP_NAME, "ok"); else property_set("ctl.start", FIRMWARE_LOADER); sched_yield(); while (count-- > 0) {// 判斷wlan driver是否加載成功 if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) { if (strcmp(driver_status, "ok") == 0) return 0; else if (strcmp(DRIVER_PROP_NAME, "failed") == 0) { wifi_unload_driver(); return -1; } } usleep(200000); } property_set(DRIVER_PROP_NAME, "timeout"); wifi_unload_driver(); return -1; #else // 如果沒有定義WIFI_DRIVER_MODULE_PATH宏,則直接設置“wlan.driver.status”屬性值為“ok” property_set(DRIVER_PROP_NAME, "ok"); return 0; #endif } ~~~ **②、CMD_LOAD_DRIVER_SUCCESS處理流程** 下面來看DriverLoadingState是如何處理CMD_LOAD_DRIVER_SUCCESS消息的。 **WifiStateMachine.java::DriverLoadingState:processMessage** ~~~ public boolean processMessage(Message message) { switch (message.what) { case CMD_LOAD_DRIVER_SUCCESS: transitionTo(mDriverLoadedState);// 轉到DriverLoadedState break; case CMD_LOAD_DRIVER_FAILURE: transitionTo(mDriverFailedState); break; ...... case CMD_START_SUPPLICANT:// DriverLoadingState不處理此消息 case ......// 其他消息 deferMessage(message); // CMD_START_SUPPLICANT消息將放到下一個狀態中再去處理 break; default: return NOT_HANDLED; } return HANDLED; } ~~~ 由上述代碼可知,DriverLoadingState不處理CMD_START_SUPPLICANT消息,而是將其推遲到下一個狀態中再去處理。對于CMD_LOAD_DRIVER_SUCCESS,直接轉到DriverLoadedState。DriverLoadedState的enter函數僅僅打印一句簡單的日志輸出,而它對CMD_START_SUPPLICANT的處理卻比較復雜。 **③、CMD_START_SUPPLICANT處理流程** CMD_START_SUPPLICANT消息將在DriverLoaded狀態中得到處理,相關代碼如下所示。 **WifiStateMachine.java::DriverLoadedState:processMessage** ~~~ public boolean processMessage(Message message) { switch(message.what) { ...... case CMD_START_SUPPLICANT: try {// 加載wlan固件。使用了netd的SoftAp命令,可參考2.3.8節 mNwService.wifiFirmwareReload(mInterfaceName, "STA"); } ...... try {// 下面這兩個函數對應netd的InterfaceCmd命令。可參考2.3.3節 mNwService.setInterfaceDown(mInterfaceName); mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); }...... // 啟動wpa_supplicant進程。請回顧5.2.3節中WifiNative介紹 if(mWifiNative.startSupplicant(mP2pSupported)){ mWifiMonitor.startMonitoring();// 啟動WifiMonitor的Monitor線程 transitionTo(mSupplicantStartingState); // 轉到SupplicantStartingState }...... break; case CMD_START_AP: ...... default: return NOT_HANDLED; } return HANDLED; } ~~~ 由上述代碼可知DriverLoadedState處理CMD_START_SUPPLICANT消息的結果。 * wlan固件被加載。 * wpa_supplicant進程被創建,并且WifiService通過WifiMonitor和它建立了交互關系。 * WifiStateMachine狀態切換至SupplicantStartingState。該狀態的enter函數沒有開展有意義的工作。 當WifiMonitor成功連接至WPAS進程后,它將發送SUP_CONNECTION_EVENT消息給WifiStateMachine(參考5.2.3節中關于WifiMonitor的介紹)。下面就來看該消息的處理流程。 **④、SUP_CONNECTION_EVENT處理流程** SUP_CONNECTION_EVENT在SupplicantStartingState狀態中得到處理,相關代碼如下所示。 **WifiStateMachine.java::SupplicantStartingState:processMessage** ~~~ public boolean processMessage(Message message) { switch(message.what) { case WifiMonitor.SUP_CONNECTION_EVENT: setWifiState(WIFI_STATE_ENABLED);// 發送WIFI_STATE_CHANGED_ACTION廣播 mSupplicantRestartCount = 0; // 發送消息給SupplicantStateTracker狀態機。請讀者自行研究 mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); mLastBssid = null; mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; mLastSignalLevel = -1; // 設置本機IP地址 mWifiInfo.setMacAddress(mWifiNative.getMacAddress()); mWifiConfigStore.initialize(); initializeWpsDetails(); // 初始化和WPS相關的一些內容。本章將略過和WPS/P2P相關的內容 // 發送SUPPLICANT_CONNECTION_CHANGE_ACTION廣播 sendSupplicantConnectionChangedBroadcast(true); transitionTo(mDriverStartedState);// 轉到DriverStartedState break; ...... } return HANDLED; } ~~~ 結合HSM知識以及圖5-4中WifiStateMachine中各個狀態的層級關系,DriverStarted的父狀態是SupplicantStarted,所以上述代碼中transitionTo(mDriverStartedState)這一句函數調用將導致SupplicantStarted和DriverStarted的enter函數依次被調用。 首先調用的是SupplicantStarted的enter函數,相關代碼如下所示。 **WifiStateMachine.java::SupplicantStartedState:enter** ~~~ public void enter() { mIsScanMode = false;// 該變量的作用見下文 mNetworkInfo.setIsAvailable(true); // config_wifi_supplicant_scan_interval用于控制掃描間隔,默認是15000毫秒 int defaultInterval = mContext.getResources().getInteger(R.integer.config_wifi_supplicant_scan_interval); mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,defaultInterval); // 向WPAS發送“SCAN_INTERVAL 掃描間隔時間”命令 mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000); } ~~~ 接著來看DriverStartedState的enter函數。 **WifiStateMachine.java::DriverStartedState:enter** ~~~ public void enter() { mIsRunning = true; mInDelayedStop = false; updateBatteryWorkSource(null); /* 由于藍牙運行在2.4GHz頻率上,所以為了避免wlan和藍牙互相干擾,下面這個函數將告知 wlan driver藍牙是否啟用。如果是,wlan芯片會做適當調整。 */ mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive); /* 下面這兩個函數用設置國家碼和頻段。其內部是通過發送消息的方式來觸發WifiNative setCountryCode和setBand函數被調用。在WifiNative中,這兩個函數都會發送形如 “DRIVER XXX”命令給WPAS。DRIVER命令是Android平臺特有的,用于給wlan driver發送一些 定制的命令。 AOSP源碼中,hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/driver_cmd_nl80211.c 中的wpa_driver_nl80211_driver_cmd函數可用于處理針對博通wlan driver的“DRIVER XXX”命令。 我們在第4章中沒有介紹相關的命令,不過它們難度并不大。請讀者以上述driver_cmd_nl80211.c 為參考文件,自行分析相關的DRIVER命令。 */ setCountryCode();setFrequencyBand(); setNetworkDetailedState(DetailedState.DISCONNECTED); // 下面三個函數都和WPAS中的“DRIVER XXX”命令有關 mWifiNative.stopFilteringMulticastV6Packets(); if (mFilteringMulticastV4Packets.get()){ mWifiNative.startFilteringMulticastV4Packets(); } else { mWifiNative.stopFilteringMulticastV4Packets(); } /* mIsScanMode默認為FALSE。該變量只能通過CMD_SET_SCAN_TYPE消息來修改。mIsScanMode 和4.5.3節“wpa_supplicant_scan分析之一”中提到的ap_scan變量有關,該變量可取值如下。 值為1:表示WPAS來完成AP掃描和選擇的絕大部分工作(包括關聯、EAPOL認證等工作)。 值為0:表示驅動完成AP掃描和選擇的工作。 值為2:和0類似,不過在NDIS(Windows上的網絡設備驅動)中用得較多。 下面代碼中的SCAN_ONLY_MODE對應值為2,而CONNECT_MODE對應值為1。 */ if (mIsScanMode) { mWifiNative.setScanResultHandling(SCAN_ONLY_MODE); mWifiNative.disconnect(); transitionTo(mScanModeState); } else { mWifiNative.setScanResultHandling(CONNECT_MODE); mWifiNative.reconnect();// 發送“RECONNECT”命令給WPAS mWifiNative.status();// 發送“STATUS”命令給WPAS transitionTo(mDisconnectedState);// 進入DisconnectedState } if (mScreenBroadcastReceived.get() == false) { PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); handleScreenStateChanged(powerManager.isScreenOn()); } else { // 發送“DRIVER SETSUSPENDMODE”命令。該命令由Driver廠商提供的庫來實現 mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()); } mWifiNative.setPowerSave(true);// 和P2P PowerSave有關。本章不討論 // 如果支持P2P,則通過mWifiP2pChannel向WifiP2p模塊發送消息 if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P); } ~~~ 上述代碼執行完后,WifiStateMachine將轉入DisconnectedState。由于DisconncedState的父狀態是ConnectModeState,它的enter函數沒有做任何有意義的工作,所以此處只介紹DisconnectedState的enter函數。 **WifiStateMachine.java::DisconnectedState:enter** ~~~ public void enter() { // 下面這段代碼和P2P有關 if (mTemporarilyDisconnectWifi) { mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE); return; } mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS, mDefaultFrameworkScanIntervalMs); /* 當系統支持后臺掃描時,如果手機屏幕關閉,則設置mEnableBackgroudScan為true以啟動后臺掃描。 mScanResultIsPending用于表示WifiService是否在等待掃描請求的結果。由于啟動后臺掃描的時候 會先取消上一次的掃描請求,所以如果mScanResultIsPending為true的話,則先不啟用后臺掃描。 */ if (mEnableBackgroundScan){ if (!mScanResultIsPending) { mWifiNative.enableBackgroundScan(true); } else {// 設置定時掃描任務。到時間后,AlarmManager將發送一個"ACTION_START_SCAN"Intent // 而WifiStateMachine對該Intent的處理就是調用startScan函數 setScanAlarm(true); } /* 如果當前沒有P2P連接,并且沒有之前保存的AP信息,則發送CMD_NO_NETWORKS_PERIODIC_SCAN消息 以觸發掃描。 */ if (!mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); } } ~~~ **⑤、setWifiEnabled流程總結** 筆者初次接觸setWifiEnabled函數的流程時,心中的感覺是“一個函數引發的一連串血案”。確實,WifiStateMachine的設計自有獨到之處,但是否有些過于復雜了呢? 沒找到一種合適的方法用圖來描述整個流程,只能用以下文字來描述。 1. WifiService的setWifiEnabled函數將調用WifiStateMachine中的同名函數。在WifiStateMachine中,CMD_LOAD_DRIVER和CMD_START_SUPPLICANT兩個消息將發送給狀態機去執行。WifiStateMachine最初的狀態是DriverUnloadedState。 2. DriverUnloadedState接收到CMD_LOAD_DRIVER消息后將轉入DriverLoadingState。而DriverLoadingState的enter函數將創建一個工作線程來執行WifiNative的loadDriver以加載Wlan驅動。如果driver加載成功,該線程會發送CMD_LOAD_DRIVER_SUCCESS消息給狀態機。 3. DriverLoadingState將在其processMessage中處理CMD_START_SUPPLICANT和CMD_LOAD_DRIVER_SUCCESS消息。其中,DriverLoadingState會延遲對CMD_START_SUPPLICANT的處理。而對于CMD_LOAD_DRIVER_SUCCESS,DriverLoadingState將直接轉入DriverLoadedState。 4. DriverLoadedState將繼續處理CMD_START_SUPPLICANT。在其processMessage中,wpa_supplicant進程將被啟動,并且WifiMonitor將建立WifiService和WPAS的關系。同時,狀態機將轉入SupplicantStartingState。另外,當WifiMonitor成功連接上WPAS后,它將發送一個SUP_CONNECTION_EVENT消息。 5. SupplicantStartingState將處理SUP_CONNECTION_EVENT消息。這些處理包括設置初始化WPS相關的信息、設置WifiState、發送消息給SupplicantStateTracker狀態機、初始化WifiConfigStore等。最后,SupplicantStartingState將轉入DriverStartedState。DriverStartedState的父狀態是SupplicantStartedState。所以這兩個狀態的enter函數均會被調用。 6. SupplicantStartedState在其enter函數中將設置掃描間隔。而DriverStartedState在其enter函數中將完成諸如Country Code、Frequency Band、Bluetooth共存模式等設置工作。有些工作需要發送形如"DRIVER XXX"的命令給WPAS。最后,SupplicantStartedState將轉入DisconnectedState。 7. DisconnectedState的enter函數將被執行(其父狀態ConnectModeState的enter函數沒有完成什么實質性的工作)。該函數主要完成了后臺掃描及定時掃描工作的一些設置。 接著來看第二個關鍵函數startScanActive。 **2、startScanActive函數分析** startScanActive定義在WifiManager中,它將調用WifiService的startScan函數,而WifiService又會調用WifiStateMachine的startScan,所以本節直接從WifiStateMachine開始。 **WifiStateMachine.java::startScan** ~~~ public void startScan(boolean forceActive) { // 對于startScanActive來說,forceActive的值為true sendMessage(obtainMessage(CMD_START_SCAN, forceActive ? SCAN_ACTIVE : SCAN_PASSIVE, 0)); } ~~~ **①、CMD_START_SCAN處理流程** WifiStateMachine當前處于DisconnectedState,故其processMessage函數將被調用以處理CMD_START_SCAN消息。相關代碼如下所示。 **WifiStateMachine.java::DisconnectedState:processMessage** ~~~ public boolean processMessage(Message message) { boolean ret = HANDLED; switch (message.what) { ...... case CMD_START_SCAN: // 取消后臺掃描 if (mEnableBackgroundScan) mWifiNative.enableBackgroundScan(false); ret = NOT_HANDLED; // 注意返回值 break; case WifiMonitor.SCAN_RESULTS_EVENT:// 掃描完畢后將收到此消息 if (mEnableBackgroundScan && mScanResultIsPending) mWifiNative.enableBackgroundScan(true); ret = NOT_HANDLED;// 注意返回值 break; ...... } return ret; } ~~~ 上述代碼重點展示了CMD_START_SCAN和SCAN_RESULTS_EVENT消息的處理情況。可知DisconnectedState都將返回NOT_HANDLED。如此,其父狀態的processMessage將被調用。沿著圖5-4 WifiStateMachine各狀態層次關系圖并結合代碼,最終DisconnectedState的祖父DriverStartedState將被觸發,相關代碼如下所示。 **WifiStateMachine.java::DriverStartedState:processMessage** ~~~ public boolean processMessage(Message message) { switch(message.what) { ...... case CMD_START_SCAN: boolean forceActive = (message.arg1 == SCAN_ACTIVE); // 對主動掃描(Active Scan)來說,setScanMode將發送“DRIVER SCAN-ACTIVE”命令 if (forceActive && !mSetScanActive){ mWifiNative.setScanMode(forceActive); } mWifiNative.scan();// 發送“SCAN”命令給WPAS以觸發掃描 if (forceActive && !mSetScanActive){ mWifiNative.setScanMode(mSetScanActive); } mScanResultIsPending = true;// 設置mScanResultIsPending為true break; ...... } return HANDLED; } ~~~ 當WPAS掃描完畢后,它將通知WifiMonitor。而WifiMonitor的handleEvent函數將向WifiStateMachine發送SCAN_RESULTS_EVENT消息(請參考5.2.3節介紹的WifiMonitor中的handleEvent函數)。下面來看該消息的處理流程。 **②、SCAN_RESULTS_EVENT處理流程** WifiStateMachine的狀態是DisconnectedState,由上一節對該狀態processMessage函數的介紹可知,對于SCAN_RESULTS_EVENT消息,DisconnectedState將返回NOT_HANDLED。所以其父狀態ConnectModeState將接著處理此消息。 **WifiStateMachine.java::ConnectModeState:processMessage** ~~~ public boolean processMessage(Message message) { ...... switch(message.what) { ...... case WifiMonitor.SCAN_RESULTS_EVENT: mWifiNative.setScanResultHandling(CONNECT_MODE);// 設置ap_scan return NOT_HANDLED;// 仍然返回NOT_HANDLED ...... } return HANDLED; } ~~~ 返回值NOT_HANDLED將導致ConnectModeState的父狀態DriverStartedState processMessage被調用,不過可惜的是DriverStartedState壓根就不處理該消息。所以還得來看DriverStartedState的父狀態SupplicantStartedState。相關代碼如下所示。 **WifiStateMachine.java::SupplicantStartedState:processMessage** ~~~ public boolean processMessage(Message message) { ...... switch(message.what) { ...... case WifiMonitor.SCAN_RESULTS_EVENT: // 從WPAS中獲取掃描結果,并保存到mScanResults變量中 setScanResults(mWifiNative.scanResults()); // 發送SCAN_RESULTS_AVAILABLE_ACTION廣播 sendScanResultsAvailableBroadcast(); mScanResultIsPending = false; break; ...... } return HANDLED; } ~~~ 和setWifiEnabled函數相比,startScanActive涉及的代碼比較簡單。而且,與該流程相關狀態主要是DisconnectedState以及其祖先狀態。 下面來看最后一個函數即connect的處理流程。 **3、connect函數分析** 從WifiManager開始,相關代碼如下所示。 **WifiManager.java::connect** ~~~ public void connect(WifiConfiguration config, ActionListener listener) { ......// 參數檢查 // 通過AsyncChannel向WifiService發送消息 message的第二個參數為INVALID_NETWORK_ID,其值為-1 mAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID, putListener(listener), config); } ~~~ WifiService接收到CONNECT_NETWORK消息后,將直接把它轉發給WifiStateMachine。下面來看WifiStateMachine是如何處理CONNECT_NETWORK的。 **①、CONNECT_NETWORK處理流程** DisconnectedState的父狀態ConnectModeState將處理CONNECT_NETWORK消息,相關代碼如下所示。 **WifiStateMachine.java::ConnectModeState:processMessage** ~~~ public boolean processMessage(Message message) { switch(message.what) { ...... case WifiManager.CONNECT_NETWORK: int netId = message.arg1;// netId為INVALID_NETWORK_ID WifiConfiguration config = (WifiConfiguration) message.obj; if (config != null) {// saveNetwork是關鍵函數,見下文分析 NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config); netId = result.getNetworkId(); } /* 下面這段代碼中: selectNetwork:選擇netId對應的無線網絡。該函數的工作和上面的saveNetwork有些類似。 reconnect將發送“RECONNECT”命令給WPAS,而WPAS的處理就是調用 wpa_supplicant_req_scan,讀者可參考4.5.3節。 sendMessage:發送CONNECT_NETWORK消息給SupplicantSTateTracker。 replyToMessage:WifiStateMachine中也有一個AsyncChannel,不過它沒有 連接到任何Handler。該函數將把CONNECT_NETWORK_SUCCEEDED發給 CONNECT_NETWORK消息的發送者(即WifiManager)。 請讀者自行研究WifiManager對CONNECT_NETWORK_SUCCEEDED消息的處理流程。 */ if (mWifiConfigStore.selectNetwork(netId) && mWifiNative.reconnect()) { mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK); replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); // 切換到DisconnectingState // 考慮到手機之前可能連接到其他AP,所以此處要先進入DisconnectingState transitionTo(mDisconnectingState); } else { ......// 失敗處理 } break; ...... } return HANDLED; } ~~~ 上述代碼中,saveNetwork比較關鍵,其代碼如下所示。 **WifiConfigStore.java::saveNetwork** ~~~ NetworkUpdateResult saveNetwork(WifiConfiguration config) { /* 如果networkId為-1,并且該無線網絡的SSID為空,則不能添加無線網絡。 用戶在WifiSettings選擇的目標無線網絡如果之前已經在wpa_supplicant.conf文件中有信息, 則它的networkid不為-1。 */ if (config == null || (config.networkId == INVALID_NETWORK_ID && config.SSID == null)) return new NetworkUpdateResult(INVALID_NETWORK_ID); // 假設本例中該無線網絡是新搜索到的,則newNetwork為true boolean newNetwork = (config.networkId == INVALID_NETWORK_ID); /* addOrUpdateNetworkNative將觸發"ADD_NETWORK"、"SET_NEWTORK id param value"等一系列 命令。這些命令和4.5節開頭介紹的一樣。請讀者自行研究addOrUpdateNetworkNative函數。 */ NetworkUpdateResult result = addOrUpdateNetworkNative(config); int netId = result.getNetworkId(); if (newNetwork && netId != INVALID_NETWORK_ID) { mWifiNative.enableNetwork(netId, false);// 發送“ENABLE_NETWORK id”給WPAS mConfiguredNetworks.get(netId).status = Status.ENABLED; } mWifiNative.saveConfig(); // 發送“SAVE_CONFIG”命令,WPAS將保存wpa_config信息到配置文件 // 發送廣播 sendConfiguredNetworksChangedBroadcast(config, result.isNewNetwork() ?WifiManager.CHANGE_REASON_ADDED : WifiManager.CHANGE_REASON_CONFIG_CHANGE); return result; } ~~~ 仔細研究selectNetwork和saveNetwork的代碼,感覺selectNetwork重復做了一些saveNetwork已經做過的工作,例如selectNetwork也會調用addOrUpdateNetworkNative函數,筆者覺得這段代碼應該有優化余地。 另外,WPAS在"ENABLE_NETWORK"過程中,會歷經一系列復雜的過程直到加入目標無線網絡(讀者可回顧4.5.3節ENABLE_NETWORK命令處理)。在這個過程中,WPAS的狀態(即wpa_sm狀態機的狀態)也會跟著發生變化。這些變化將觸發WifiMonitor向WifiStateMachine發送SUPPLICANT_STATE_CHANGE_EVENT消息。由于篇幅問題,本章不擬討論這些消息的處理過程。感興趣的讀者不妨學完本章后再來研究它們。 當WPAS成功加入目標無線網絡后,它將發送信息給WifiMonitor例如: ~~~ CTRL-EVENT-CONNECTED-Connection to 00:1e:58:ec:d5:6d completed(reauth)[id=1 id_str=] ~~~ 而這個信息將觸發WifiMonitor發送NETWORK_CONNECTION_EVENT消息給WifiStateMachine。所以此處直接來分析NETWORK_CONNECTION_EVENT消息的處理流程即可。 **②、NETWORK_CONNECTION_EVENT消息處理流程** 此時WifiStateMachine處于DisconnectingState,不過它并不處理NETWORK_CONNECTION_EVENT消息,所以該消息最終將由其父狀態ConnectModeState處理。相關代碼如下所示。 **WifiStateMachine.java::ConnectModeState:processMessage** ~~~ public boolean processMessage(Message message) { ...... switch(message.what) { ...... case WifiMonitor.NETWORK_CONNECTION_EVENT: mLastNetworkId = message.arg1;// arg1指向目標AP的ID mLastBssid = (String) message.obj;// obj指向目標AP的bssid mWifiInfo.setBSSID(mLastBssid); mWifiInfo.setNetworkId(mLastNetworkId); setNetworkDetailedState(DetailedState.OBTAINING_IPADDR); // 發送NETWORK_STATE_CHANGED_ACTION廣播 sendNetworkStateChangeBroadcast(mLastBssid); transitionTo(mObtainingIpState);// 進入ObtainingIpstate狀態 break; ...... } return HANDLED; } ~~~ 來看ObtaingIpState的enter函數(注意,ObtaingIpState的父狀態是L2ConnectedState,故父狀態的enter函數先執行。L2ConnectedState的enter函數比較簡單,此處略),代碼如下所示。 **WifiStateMachine.java::ObtaingIpState:enter** ~~~ public void enter() { // 判斷目標AP是否使用靜態IP配置 if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) { // 本例中的目標AP用得是動態IP配置。所以下面還要創建一個DhcpStateMachine對象 if (mDhcpStateMachine == null){ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext, WifiStateMachine.this, mInterfaceName); } mDhcpStateMachine.registerForPreDhcpNotification(); // 向DhcpStateMachine發送CMD_START_DHCP消息 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); } else { ......// 靜態IP的處理流程 } } ~~~ DhcpStateMachine是和DHCP相關的一個狀態機對象,由于其內容比較簡單,本章不詳述。在DhcpStateMachine運行過程中,它將向WifiStateMachine發送兩個消息CMD_PRE_DHCP_ACTION和CMD_POST_DHCP_ACTION,它們均由ObtainingIpState的父狀態L2ConnectedState處理。 **③、CMD_PRE/POST_DHCP_ACTION處理流程** **WifiStateMachine.java::L2ConnectedState:processMessage** ~~~ public boolean processMessage(Message message) { ...... switch (message.what) { case DhcpStateMachine.CMD_PRE_DHCP_ACTION: // 處理dhcp交互之前的一些工作,例如設置藍牙共存模式,關閉p2p powersave等功能等 handlePreDhcpSetup(); // 向DhcpStateMachine發送CMD_PRE_DHCP_ACTION_COMPLETE消息。DhcpStateMachine將 // 啟動dhcpcd進程以從AP那獲取一個IP地址。如果一切順利,它將發送CMD_POST_DHCP_ACTION // 消息給WifiStateMachine mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE); break; case DhcpStateMachine.CMD_POST_DHCP_ACTION: // 和handlePreDhcpSetup相對應,恢復藍牙共存模式及打開P2P PowerSave功能 handlePostDhcpSetup(); if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) { // 下面這個函數將發送LINK_CONFIGURATION_CHANGED_ACTION廣播 // 本章不討論和該廣播相關的處理流程 handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj); transitionTo(mVerifyingLinkState);// 轉入VerifyingLinkState } ...... break; ...... } return HANDLED; } ~~~ 在L2ConnectedState中,WPAS其實已經連接上了AP。當收到CMD_POST_DHCP_ACTION消息時,手機也從AP那得到了一個IP地址。不過,WifiService還沒有完成其最終的工作,它將轉入VerifyingLinkState。該狀態將會和一個名為WifiWatchdogStateMachine的對象交互。 >[info] 提示 WifiWatchdogStateMachine用于監控無線網絡的信號質量,5.4節將詳細介紹。 先來看VeryfingLinkState的代碼,如下所示。 **WifiStateMachine.java::VeryfingLinkState** ~~~ class VerifyingLinkState extends State { public void enter() { setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK); mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK); sendNetworkStateChangeBroadcast(mLastBssid); } public boolean processMessage(Message message) { switch (message.what) { case WifiWatchdogStateMachine.POOR_LINK_DETECTED: break; case WifiWatchdogStateMachine.GOOD_LINK_DETECTED: // 如果WifiWatchdogStateMachine判斷此時無線網絡的信號良好 // 它將發送GOOD_LINK_DETECTED消息給WifiStateMachine transitionTo(mCaptivePortalCheckState);// 轉入CaptivePortalCheckState break; default: return NOT_HANDLED; } return HANDLED; } } ~~~ CaptivePortalCheckState是Android 4.2新引入的一個狀態。CaptivePortalCheckState和一種名為Captive Portal(強制網絡門戶)認證方法有關。它對應如下一種應用場景:當未認證用戶初次上網時,系統將強制用戶打開某個特定頁面,例如運營商指定的頁面。在該頁面中,用戶必須點擊“同意”按鈕后才能真正使用網絡。該認證方法也叫Portal認證。目前在一些公共場所(如機場、酒店)中被大量使用。關于Capitve Portal的詳細信息,讀者可閱讀參考資料[1]。 >[info] 提示 后面將詳細介紹Android 4.2代碼中對Captive Portal Check的處理流程。 下面來看CaptivePortalCheckState的代碼,如下所示。 **WifiStateMachine.java::CaptivePortalCheckState** ~~~ class CaptivePortalCheckState extends State { public void enter() { setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK); // 設置DetailedState為CAPTIVE_PORTAL_CHECK mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK); // 發送NETWORK_STATE_CHANGED_ACTION廣播。請讀者記住此處的調用 sendNetworkStateChangeBroadcast(mLastBssid); } public boolean processMessage(Message message) { switch (message.what) { case CMD_CAPTIVE_CHECK_COMPLETE: // 檢查完畢。詳情見“Captive Portal Check介紹” try { mNwService.enableIpv6(mInterfaceName); } ...... setNetworkDetailedState(DetailedState.CONNECTED); mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED); sendNetworkStateChangeBroadcast(mLastBssid); transitionTo(mConnectedState);// 終于進入ConnectedState break; default: return NOT_HANDLED; } return HANDLED; } } ~~~ 由上述代碼可知,當Captive Portal檢查完畢后,CaptivePortalCheckState將收到CMD_CAPTIVE_CHECK_COMPLETE消息。在該消息的處理過程中,WifiStateMachine終于轉入ConnectedState,也就是本次旅程的終點。 ConnectedState僅處理POOR_LINK_DETECTE消息,相關代碼比較簡單,不贅述。 下面來總結connect的流程。 **④、connect流程總結** connect的流程包括如下幾個關鍵點。 * 整個流程起源于WifiManager向WifiStateMachine發送的CONNECT_NETWORK消息。 * ConnectModeState將處理此消息。在其處理過程中,它將發送一系列命令給WPAS,而WPAS將完成802.11規范中所定義的身份認證、關聯、四次握手等工作。ConnectModeState隨之轉入DisconnectingState。 * 當WPAS加入目標無線AP后,它將發送NETWORK_CONNECTION_EVENT給WifiStateMachine。DisconnectingState的父狀態ConnectModeState將處理此消息,具體處理過程比較簡單。最終,WifiStateMachine將轉入ObtaingIpState。 * 在ObtaingIpState的enter函數中,DhcpStateMachine對象將被創建。DhcpStateMachine和dpcpcd有關。相關代碼比較簡單,請讀者自行閱讀。在WifiStateMachine和DhcpStateMachine的交互過程中,DhcpStateMachine將向WifiStateMachine發送CMD_PRE_DHCP_ACTION和CMD_POST_DHCP_ACTION消息。在CMD_POST_DHCP_ACTION消息的處理過程中,WifiStateMachine將轉入VerifyingLinkState。 * VerifyingLinkState將和WifiWatchdogStateMachine交互。WifiWatchdogStateMachine用于監控無線網絡信號的好壞。如果一切正確,它將轉入CaptivePortalCheckState。 * CaptivePortalCheckState用于檢查目標無線網絡提供商是否需要Captive Portal Check。如果一切正常,WifiStateMachine最終將轉入ConnectedState。該狀態就是本章第二條分析路線的終點。 下面介紹本章最后兩個重要知識點,WifiWatchdogStateMachine和Captive Portal Check。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看