admin管理员组文章数量:1620918
一.背景介绍
当用户打开wifi,扫描完成之后,当用户点击AP列表中一项并输入正确的密码后,就可以开始AP的连接过程了.点击连接到最终连接成功,这个过程中具体流程是如何实现的,这篇文章,将介绍一下这个流程;其用户界面显示过程如下:
二.流程跟踪
我们现有的AP有两种方式:有密码保护的连接和无密码保护的连接。其实这两种方式的主要区别在于有密码保护的AP连接过程需要在UI上弹出一个输入密码的界面给用户。输入密码之后点击连接就和无密码保护的连接过程的总体流程是一样的,只不过有密码保护的调用的方法多一点。
2.1 代码流程
在LINUX/android/packages/apps/Settings/res/values/strings.xml文件中对于connect有如下的代码:
<string name="wifi_menu_connect">Connect to network</string> |
在LINUX/android/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java文件中对于Connect选项有如下的代码:
public class WifiSettings extends RestrictedSettingsFragment implements View.OnClickListener, DialogInterface.OnClickListener {
private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) { menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect); }
public boolean onContextItemSelected(MenuItem item) { case MENU_ID_CONNECT: { if (mSelectedAccessPointworkId != INVALID_NETWORK_ID) { mWifiManager.connect(mSelectedAccessPointworkId, mConnectListener); } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) { mSelectedAccessPoint.generateOpenNetworkConfig(); mWifiManager.connect(mSelectedAccessPoint.getConfig(), mConnectListener); } else { showDialog(mSelectedAccessPoint, true); } return true; } } |
INVALID_NETWORK_ID值的意思是当前连接的AP是一个新的AP(之前没有连接过);从上面的代码可以看出当点击连接之后代码会首先对AP是否是新的AP连接和是否有密码保护进行判断,这两种情况都会直接调用connect函数进行连接。如果有密码的AP连接则显示密码窗口进行连接,关于这部分我们将在第二节进行分析;接下来我们分析无密码或者新AP的AP连接过程;调用LINUX/android/frameworks/base/wifi/
java/android/net/wifi/WifiManager.java中的connect函数,如下:
public void connect(WifiConfiguration config, ActionListener listener) { writeBackTrace(); validateChannel(); sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID, putListener(listener), config); } |
其中第一个参数WifiConfiguration是当前需要连接的AP的配置信息,包括SSID、BSSID、密码以及加密方式等信息;ActionListern作为callback来通知客户程序connect方法是否调用成功,这里的调用成功只是指参数是否正确,并不表示AP是否连接成功。在LINUX/android/frameworks/base/wifi/java/android/net/wifi/
WifiService.java中去看看如果处理CONNECT_NETWORK这个消息:
case WifiManager.CONNECT_NETWORK: { if (config != null && config.isValid()) { if (config.proxySettings != ProxySettings.PAC) { mWifiStateMachine.sendMessage(Message.obtain(msg)); } } |
WifiService将这个消息传递给WifiStateMachine处理,这时候LINUX/android/frameworks/base/wifi/java/
android/net/wifi/WifiStateMachine.java的ConnectModeState将处理CONNECT_NETWORK这个消息,代码如下:
case WifiManager.CONNECT_NETWORK: if (config != null) { NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config); } if (mWifiConfigStore.selectNetwork(netId) && mWifiNative.reconnect()) { mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK); replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); transitionTo(mDisconnectingState); } |
这里主要调用了下面几个函数来进行AP的连接:WifiConfigStore.saveNetwok(config)将AP的配置信息写入到wpa_supplicant.conf中;WifiConfigStore.selectNetwork(netId)用于enable即将要连接的AP,而disable掉其它的AP;WifiNative.reconnect()发起重新连接的请求给wpa_supplicant。同时发送了两个消息,一个是给wpa_suppicant发送的消息;SupplicantStateTracker用于监视wpa_supplicant的状态;replyToMessage用于给WifiSettings发送消息,说明connect函数已经调用成功;接着transition到DisconnectingState中,来看看DisconnectingState,这个状态code比较少:
class DisconnectingState extends State { @Override public boolean processMessage(Message message) { case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: deferMessage(message); handleNetworkDisconnect(); transitionTo(mDisconnectedState); break; default: return NOT_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。NOT_HANDLED的意思是没有处理这种情况下将会交给起父状态去处理;当Wifi和AP之间已经连接成功后,WifiMonitor就会收到wpa_supplicant发送上来的CTRL-EVENT-CONNECTED这个event,WifiMonitor收到这个消息后,会向WifiStateMachine发送NETWORK_CONNECTION_EVENT表示已经和AP之间成功的连线,WifiStateMachine的ConnectModeState会来处理这个消息,代码如下:
case WifiMonitor.NETWORK_CONNECTION_EVENT: transitionTo(mObtainingIpState); break; |
上面的代码可以看出连接成功之后将会转到IP申请阶段;进入到ObtainingIpState状态,到ObtainingIpState的enter函数:
class ObtainingIpState extends State { @Override public void enter() { if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) { startDhcp(); } else { 省略代码 } } } |
ObtainingIpState就是获取IP的状态,这里分为两种获取IP的方式,一种是用户静态配置的,另一种是通过DHCP动态分配。这里只看动态分配的,进到到startDhcp去分析:
void startDhcp() { mDhcpStateMachine.registerForPreDhcpNotification(); mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); } |
首先去创建DhcpStateMachine的实例,然后向它发送一个CMD_START_DHCP的命令,LINUX/android/
frameworks/base/core/java/android/net/DhcpStateMachine.java初始化完毕后,对CMD_START_DHCP进行处理,代码如下:
case CMD_START_DHCP: if (mRegisteredForPreDhcpNotification) { mController.sendMessage(CMD_PRE_DHCP_ACTION); transitionTo(mWaitBeforeStartState); } |
收到CMD_START_DHCP消息就会马上向WifiStateMachine发送CMD_PRE_DHCP_ACTION表示DhcpStateMachine马上就要开始发送discovery或者renew的封包了,来看WifiStateMachine收到这个消息的处理:
class L2ConnectedState extends State { public boolean processMessage(Message message) { switch (message.what) { case DhcpStateMachine.CMD_PRE_DHCP_ACTION: handlePreDhcpSetup(); break; } } }
void handlePreDhcpSetup() { mDhcpActive = true; if (!mBluetoothConnectionActive) { mWifiNative.setBluetoothCoexistenceMode( mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); }
msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE; mWifiP2pChannel.sendMessage(msg); } |
在处理CMD_PRE_DHCP_ACTION中调用handlePreDhcpSetup函数主要是设置BT共存模式、关闭P2P
Powersave等功能,并且WifiStateMachine会向LINUX/android/frameworks/base/core/java/android/net/
DhcpStateMachine.java发送CMD_PRE_DHCP_ACTION_COMPLETE,用于指示前期准备工作已经做好了,当DhcpStateMachine收到CMD_PRE_DHCP_ACTION_COMPLETE命令后就可以开始dhcp的discovery/reponse了,用于两端来获取IP,代码如下:
class WaitBeforeStartState extends State {
case CMD_PRE_DHCP_ACTION_COMPLETE: if (runDhcp(DhcpAction.START)) { transitionTo(mRunningState); } else { transitionTo(mStoppedState); } break; }
private boolean runDhcp(DhcpAction dhcpAction) {
boolean success = false; DhcpResults dhcpResults = new DhcpResults(); dhcpResults.autoip = getAutoIpSettings(); if (dhcpAction == DhcpAction.START) { NetworkUtils.stopDhcp(mInterfaceName); success = NetworkUtils.runDhcp(mInterfaceName, dhcpResults); } mDhcpResults = dhcpResults; mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults) .sendToTarget(); return success; } |
如上的代码会对CMD_PRE_DHCP_ACTION_COMPLETE消息进行处理,会调用runDhcp函数进行获取IP的操作。当IP成功获取后,DhcpStateMachine会给WifiStateMachine发送CMD_POST_DHCP_ACTION消息,WifiStateMachine收到消息后做如下的处理:
class L2ConnectedState extends State { case DhcpStateMachine.CMD_POST_DHCP_ACTION: handlePostDhcpSetup(); if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) { handleSuccessfulIpConfiguration((DhcpResults) message.obj); transitionTo(mVerifyingLinkState); } } |
其中arg1表示是成功还是失败,如果成功,调用WifiStateMachine自身的handleSuccessfulIpConfiguration来处理,并transition 到VerifyingLinkState中;如果失败则会transition到DisconectingState中。这里只看成功获取IP的情况,进入到VerifyingLinkState中:
class VerifyingLinkState extends State { public boolean processMessage(Message message) { switch (message.what) { case WifiWatchdogStateMachine.POOR_LINK_DETECTED: //stay here break; case WifiWatchdogStateMachine.GOOD_LINK_DETECTED: transitionTo(mCaptivePortalCheckState); break; default: 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 { public boolean processMessage(Message message) { switch (message.what) { case CMD_CAPTIVE_CHECK_COMPLETE: mNwService.enableIpv6(mInterfaceName); setNetworkDetailedState(DetailedState.CONNECTED); mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED); sendNetworkStateChangeBroadcast(mLastBssid); transitionTo(mConnectedState); break; } return HANDLED; } } |
首先会发送CAPTIVE_PORTAL_CHECK的broadcast,这个会被WifiStateTracker接收并处理,然后调用ConnectivityService的接口去处理captive portal相关的内容.当ConnectivityService完成captive portal check后,就会给WifiStateMachine发送CMD_CAPTIVE_CHECK_COMPLETE消息,就会跳转到ConnectedState表示连接过程的结束了。
2.2 Log流程
以下的Log中信息中我增加了很多自己的Log标志,主要目的是要清晰地根据Log看出代码的流程;以下是Log的内容,有些粗糙还望海涵;
writeBackTrace函数中判断isWriteBackTraceEnabled为真之后就会打印这条Log,并且创建Throwable的对象以下为创建时所打印的Log信息; 第一部分:进入WifiManager中的connect函数首先调用writeBackTrace函数,会打印第一部分;
第二部分:WifiService收到CONNECT_NETWORK消息消息之后,首先打印config的内容,这部分主要是config的内容;接着会将CONNECT_NETWORK消息发送给WifiStateMachine处理;
接下来看一下 WifiStateMachine对CONNECT_NETWORK消息的处理Log信息(具体的解释请砍Log后面的注释):
在完成上述的操作之后,在addOrUpdateNetworkNative函数中调用WifiNative文件中setNetworkVariable函数去设置网络的一些参数。当addOrUpdateNetworkNative函数完成SET之后会调用readNetworkVariables函数,在readNetworkVariables函数中会调用WifiNative文件中的getNetworkVariable函数去获取网络参数的操作,由于获取参数比较得多我们此处的Log信息只列出一部分:
完成获取之后,在saveNetwork函数其次会调用WifiNative文件中的enableNetwork函数,就会发送ENABLE_NETWORK命令给wpa_supplicant;wpa_supplicant在处理ENABLE_NETWORK命令的时候回去发送连接的请求;
发送了连接请求之后,接着在saveNetwork函数会调用WifiNative文件中的saveConfig函数,就会下发SAVE_CONFIG给wpa_supplicant。wpa_supplicant会返回要连接的AP的信息;
WifiStateMachine收到CONNECT_NETWORK消息之后会其次调用WifiConfigStore文件中的selectNetwork函数;selectNetwork函数会首先调用(1)addOrUpdateNetworkNative函数,接下来又是上面的那一套SET和GET的调用;因与前面的调用一样(个人觉得此处是重复,但不知道设计者有什么深层的含义)所以此处只列出一部分Log信息。完成 SET和GET的调用之后,selectNetwork函数会其次调用(2)WifiNative文件中的saveConfig函数向wpa_supplicant发送命令SAVE_CONFIG;此处与前面的操作又重复;
selectNetwork函数接着调用enableNetworkWithoutBroadcast函数,enableNetworkWithoutBroadcast调用WifiNative文件中的enableNetwork函数向wpa_supplicant发送命令SELECT_NETWORK;
WifiStateMachine收到CONNECT_NETWORK消息之后会最后调用WifiNative文件中的reconnect函数;reconnect函数会向wpa_supplicant发送RECONNECT命令;之后就是对connect的处理;
接下来就是连接操作,以及Ip的申请此处我只说到成功连接的部分;关于其他的我不再在此说明;到CTRL-EVENT-CONNECTED这条Log信息说明Ap已经链接,之后就是Ip的申请等等操作;
三.流程图
版权声明:本文标题:Android WIFI认证的流程 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1728818336a1175163.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论