原文地址:http://blog.csdn.net/lilian0118/article/details/22408287 (找了很久,這是Android 4.4版本的,更多可以去作者博客觀看)
本章主要介紹用戶手動的在Settings中點擊Scan和Connect按鈕,輸入密碼後的連接過程,先看整體流程圖:
WiFi Scan過程分析
- public boolean startScan(WorkSource workSource) {
- try {
- mService.startScan(workSource);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
- WifiService.java
- public void startScan(WorkSource workSource) {
- enforceChangePermission();
- if (workSource != null) {
- enforceWorkSourcePermission();
- // WifiManager currently doesn't use names, so need to clear names out of the
- // supplied WorkSource to allow future WorkSource combining.
- workSource.clearNames();
- }
- mWifiStateMachine.startScan(Binder.getCallingUid(), workSource);
- }
WiFiStateMachine的startScan方法會給自己發送一個CMD_START_SCAN的message,由前面toggle on wifi的知識,這個消息將由DisconnectedState及其父State來處理,從代碼中可以很容易的分析到,CMD_START_SCAN將會被DriverStartedState 來處理,進入到處理的代碼中:
- public boolean processMessage(Message message) {
- switch(message.what) {
- case CMD_START_SCAN:
- noteScanStart(message.arg1, (WorkSource) message.obj);
- startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
- break;
noteScanStart是用於通知電量統計用;startScanNative會向wpa_supplicant發送SCAN的命令,當wpa_suppliant執行完SCAN併成功找到一些AP後,就會給WifiMonitor發送CTRL-EVENT-SCAN-RESULTS的event,WifiMonitor會parse出這個event,並向WifiStateMachine發送SCAN_RESULTS_EVENT消息,WifiStateMachine的SupplicantStartedState會處理這個消息,如下:
- case WifiMonitor.SCAN_RESULTS_EVENT:
- setScanResults();
- sendScanResultsAvailableBroadcast();
- mScanResultIsPending = false;
- break;
這裏主要做了兩件事,一是去獲取scanResults,另外會發送一個廣播信息出去,如果有檢測這個廣播的receive收到這個廣播後,就可以調用函數去獲取到scanResults並顯示到listview上面,例如WifiSettings。進入到setScanResults裏面來分析:
- private void setScanResults() {
- while (true) {
- tmpResults = mWifiNative.scanResults(sid);
- if (TextUtils.isEmpty(tmpResults)) break;
- scanResultsBuf.append(tmpResults);
- scanResultsBuf.append("\n");
- String[] lines = tmpResults.split("\n");
- sid = -1;
- for (int i=lines.length - 1; i >= 0; i--) {
- if (lines[i].startsWith(END_STR)) {
- break;
- } else if (lines[i].startsWith(ID_STR)) {
- try {
- sid = Integer.parseInt(lines[i].substring(ID_STR.length())) + 1;
- } catch (NumberFormatException e) {
- // Nothing to do
- }
- break;
- }
- }
- if (sid == -1) break;
- }
- scanResults = scanResultsBuf.toString();
- if (TextUtils.isEmpty(scanResults)) {
- return;
- }
- synchronized(mScanResultCache) {
- for (String line : lines) {
- if (line.startsWith(BSSID_STR)) {
- bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
- } else if (line.startsWith(FREQ_STR)) {
- try {
- freq = Integer.parseInt(line.substring(FREQ_STR.length()));
- } catch (NumberFormatException e) {
- freq = 0;
- }
- }
- else if (line.startsWith(SSID_STR)) {
- wifiSsid = WifiSsid.createFromAsciiEncoded(
- line.substring(SSID_STR.length()));
- } else if (line.startsWith(DELIMITER_STR) || line.startsWith(END_STR)) {
- if (bssid != null) {
- String ssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
- String key = bssid + ssid;
- ScanResult scanResult = mScanResultCache.get(key);
- if (scanResult != null) {
- scanResult.level = level;
- scanResult.wifiSsid = wifiSsid;
- // Keep existing API
- scanResult.SSID = (wifiSsid != null) ? wifiSsid.toString() :
- WifiSsid.NONE;
- scanResult.capabilities = flags;
- scanResult.frequency = freq;
- scanResult.timestamp = tsf;
- } else {
- scanResult =
- new ScanResult(
- wifiSsid, bssid, flags, level, freq, tsf);
- mScanResultCache.put(key, scanResult);
- }
- mScanResults.add(scanResult);
- }
這個函數看起來比較複雜,其實仔細分析,它只是循環的parse從WifiNative獲取到AP列表信息,WifiNative.scanResut的返回結果如下,每個AP之間用"===="分割,末尾以“####”來表示結束。
bssid=68:7f:76:d7:1a:6e
freq=2412
level=-44
tsf=1344626243700342
flags=[WPA2-PSK-CCMP][WPS][ESS]
ssid=zfdy
====
id=2
bssid=68:5f:74:d7:1a:6f
req=5180
level=-73
tsf=1344626243700373
flags=[WPA2-PSK-CCMP][WPS][ESS]
ssid=zuby
####
連接AP的流程
- public void connect(WifiConfiguration config, ActionListener listener) {
- if (config == null) throw new IllegalArgumentException("config cannot be null");
- validateChannel();
- // Use INVALID_NETWORK_ID for arg1 when passing a config object
- // arg1 is used to pass network id when the network already exists
- sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
- putListener(listener), config);
- }
其中第一個參數WifiConfiguration是當前需要連接的AP的配置信息,包括SSID、BSSID、密碼以及加密方式等信息;ActionListern作爲callback來通知客戶程序connect方法是否調用成功,這裏的調用成功只是指參數是否正確,並不表示AP是否連接成功。由前面介紹的AsyncChannel的知識,到WifiService中去看看如果處理CONNECT_NETWORK這個消息:
- case WifiManager.CONNECT_NETWORK:
- case WifiManager.SAVE_NETWORK: {
- WifiConfiguration config = (WifiConfiguration) msg.obj;
- int networkId = msg.arg1;
- if (config != null && config.isValid()) {
- // This is restricted because there is no UI for the user to
- // monitor/control PAC.
- if (config.proxySettings != ProxySettings.PAC) {
- if (DBG) Slog.d(TAG, "Connect with config" + config);
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- } reak;
- }
WifiService將這個消息傳遞給WifiStateMachine處理,由前面介紹的Scan的知識,這時候WifiStateMachine的ConnectModeState將處理CONNECT_NETWORK這個消息,代碼如下:
- case WifiManager.CONNECT_NETWORK:
- int netId = message.arg1;
- config = (WifiConfiguration) message.obj;
- /* Save the network config */
- if (config != null) {
- NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
- netId = result.getNetworkId();
- }
- if (mWifiConfigStore.selectNetwork(netId) &&
- mWifiNative.reconnect()) {
- /* The state tracker handles enabling networks upon completion/failure */
- mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
- replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
- /* Expect a disconnection from the old connection */
- transitionTo(mDisconnectingState);
- }
- break;
這裏主要調用了下面幾個函數來進行AP的連接:WifiConfigStore.saveNetwok(config)將AP的配置信息寫入到wpa_supplicant.conf中;WifiConfigStore.selectNetwork(netId)用於enable即將要連接的AP,而disable掉其它的AP;WifiNative.reconnect()發起重新連接的請求給wpa_supplicant。接着transition到DisconnectingState中,來看看DisconnectingState,這個狀態code比較少:
- class DisconnectingState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_SET_OPERATIONAL_MODE:
- if (message.arg1 != CONNECT_MODE) {
- deferMessage(message);
- }
- break;
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
- * we have missed the network disconnection, transition to mDisconnectedState
- * and handle the rest of the events there
- */
- deferMessage(message);
- handleNetworkDisconnect();
- transitionTo(mDisconnectedState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
當執行完WifiNative.reconnect(),wpa_supplicant會不斷的往WifiMonitor發送包括CTRL-EVENT-STATE-CHANGE、ASSOCIATING、ASSOCIATED、FOUR_WAY_HANDSHARK、GROUP_HANDSHARK等event,WifiMonitor會不斷的去parse這些event並向WifiStatemachine發送消息,其中一個比較重要的消息就是當wpa_supplicant的狀態改變是會發送WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,上面的DisconnectiongState 收到這個消息後,會transition到DisconnectedState。
- case WifiMonitor.NETWORK_CONNECTION_EVENT:
- if (DBG) log("Network connection established");
- mLastNetworkId = message.arg1;
- mLastBssid = (String) message.obj;
- mWifiInfo.setBSSID(mLastBssid);
- mWifiInfo.setNetworkId(mLastNetworkId);
- /* send event to CM & network change broadcast */
- setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
- sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mObtainingIpState);
- break;
WifiStateMachine處理完這個消息後,會跳轉到ObtainingIpState,進到到ObtainingIpState的enter函數看看:
- class ObtainingIpState extends State {
- @Override
- public void enter() {
- if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
- // TODO: If we're switching between static IP configuration and DHCP, remove the
- // static configuration first.
- startDhcp();
- } else {
- // stop any running dhcp before assigning static IP
- stopDhcp();
- DhcpResults dhcpResults = new DhcpResults(
- mWifiConfigStore.getLinkProperties(mLastNetworkId));
- InterfaceConfiguration ifcg = new InterfaceConfiguration();
- Iterator<LinkAddress> addrs =
- dhcpResults.linkProperties.getLinkAddresses().iterator();
- if (!addrs.hasNext()) {
- loge("Static IP lacks address");
- sendMessage(CMD_STATIC_IP_FAILURE);
- } else {
- ifcg.setLinkAddress(addrs.next());
- ifcg.setInterfaceUp();
- try {
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) log("Static IP configuration succeeded");
- sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
- } catch (RemoteException re) {
- loge("Static IP configuration failed: " + re);
- sendMessage(CMD_STATIC_IP_FAILURE);
- } catch (IllegalStateException e) {
- loge("Static IP configuration failed: " + e);
- sendMessage(CMD_STATIC_IP_FAILURE);
- }
- }
- }
- }
ObtainingIpState就是獲取IP的狀態,這裏分爲兩種獲取IP的方式,一種是用戶靜態配置的,另一種是通過DHCP動態分配。這裏只看動態分配的,進到到startDhcp去分析:
- void startDhcp() {
- if (mDhcpStateMachine == null) {
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
- }
- mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
- }
首先去創建DhcpStateMachine的實例,然後向它發送一個CMD_START_DHCP的命令,DhcpStateMachine初始化完畢後,收到這個消息就會馬上向WifiStateMachine發送CMD_PRE_DHCP_ACTION表示DhcpStateMachine馬上就要開始發送discovery或者renew的封包了,來看WifiStateMachine收到這個消息的處理:
- public boolean processMessage(Message message) {
- switch (message.what) {
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
- handlePreDhcpSetup();
- break;
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
- handlePostDhcpSetup();
- if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
- if (DBG) log("DHCP successful");
- handleSuccessfulIpConfiguration((DhcpResults) message.obj);
- transitionTo(mVerifyingLinkState);
- } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
- if (DBG) log("DHCP failed");
- handleFailedIpConfiguration();
- transitionTo(mDisconnectingState);
- }
- break;
在處理完CMD_PRE_DHCP_ACTION後,WifiStateMachine會向DhcpStateMachine發送CMD_PRE_DHCP_ACTION_COMPLETE,用於指示前期準備工作已經做好了,這是就可以開始dhcp的discovery/reponse了,用於兩端來獲取IP,當IP成功獲取後,DhcpStateMachine會給WifiStateMachine發送CMD_POST_DHCP_ACTION消息,其中arg1表示是成功還是失敗,如果成功,就會調用handleSuccessfulIpConfiguration來處理,並transition 到VerifyingLinkState中;如果失敗則會transition到DisconectingState中。這裏只看成功獲取IP的情況,進入到VerifyingLinkState中:
- class VerifyingLinkState extends State {
- @Override
- public void enter() {
- log(getName() + " enter");
- setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
- mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
- sendNetworkStateChangeBroadcast(mLastBssid);
- }
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
- //stay here
- log(getName() + " POOR_LINK_DETECTED: no transition");
- break;
- case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
- log(getName() + " GOOD_LINK_DETECTED: transition to captive portal check");
- transitionTo(mCaptivePortalCheckState);
- break;
- default:
- if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
在VerifyingLinkState主要是來驗證當前連接狀況的,主要方式是通過統計信號強度以及丟包率,這些工作是交給WifiWatchdogStateMachine來做的,當WifiAP的信號強度增強或者變弱,會發送兩種消息給WifiStateMachine,一種是WifiWatchdogStateMachine.GOOD_LINK_DETECTED,另一種是WifiWatchdogStateMachine.POOR_LINK_DETECTED。當收到GOOD_LINK_DETECTED消息後,就會跳轉到CaptivePortalCheckState中;當收到的是POOR_LINK_DETECTED,則維持原來的狀態不變。我們跳轉到CaptivePortalCheckState去分析:
- class CaptivePortalCheckState extends State {
- @Override
- public void enter() {
- log(getName() + " enter");
- setNetworkDetailedState(DetailedState.CAPTIVE_PORTAL_CHECK);
- mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CAPTIVE_PORTAL_CHECK);
- sendNetworkStateChangeBroadcast(mLastBssid);
- }
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_CAPTIVE_CHECK_COMPLETE:
- log(getName() + " CMD_CAPTIVE_CHECK_COMPLETE");
- try {
- mNwService.enableIpv6(mInterfaceName);
- } catch (RemoteException re) {
- loge("Failed to enable IPv6: " + re);
- } catch (IllegalStateException e) {
- loge("Failed to enable IPv6: " + e);
- }
- setNetworkDetailedState(DetailedState.CONNECTED);
- mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mConnectedState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
首先會發送CAPTIVE_PORTAL_CHECK的broadcast,這個會被WifiStateTracker接收並處理,然後調用ConnectivityService的接口去處理captive portal相關的內容,與captive portal相關的只是可以參考:http://en.wikipedia.org/wiki/Captive_portal