WiFi toggled on流程分析
这一章来分析一下从Settings打开wifi后,framework所有的流程以及状态转换,调用的主要函数就是WiFiMananger的setWifiEnabled(boolean enabled),这个函数在前面介绍的WifiService启动流程中也有提到,当SystemServer 创建了个WifiService后,就会调用它的checkAndStartWifi,在这个函数里面,也同样会调用到setWifiEnabled(boolean enabled),这是为了恢复用户在开机之前的wifi状态。下面来详细分析WifiMananger的setWifiEnabled函数。先来看看总体的流程图
对照上面的流程图,我们从WifiMananger的setWifiEnabled来分析一下代码:
- public boolean setWifiEnabled(boolean enabled) {
- try {
- return mService.setWifiEnabled(enabled);
- } catch (RemoteException e) {
- return false;
- }
- }
- WifiService.java
- public synchronized boolean setWifiEnabled(boolean enable) {
- enforceChangePermission();
- Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- if (DBG) {
- Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
- }
- /*
- * Caller might not have WRITE_SECURE_SETTINGS,
- * only CHANGE_WIFI_STATE is enforced
- */
- long ident = Binder.clearCallingIdentity();
- try {
- if (! mSettingsStore.handleWifiToggled(enable)) {
- // Nothing to do if wifi cannot be toggled
- return true;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- mWifiController.sendMessage(CMD_WIFI_TOGGLED);
- return true;
- }
在WifiService里面,先把传进来的参数写到WifiSettingsStore里面,WifiSettingsStore调用Settings提供的ContentProviders写到Sqlite的DB里面,所以我们看到mWifiController.sendMessage(CMD_WIFI_TOGGLED)是没有把这个参数传递过去的。接着去看WifiController里面如何处理CMD_WIFI_TOGGLED,由前面的WifiService启动流程分析,我们知道CMD_WIFI_TOGGLED将由WifiController的ApStaDisabledState来处理:
- class ApStaDisabledState extends State {
- private int mDeferredEnableSerialNumber = 0;
- private boolean mHaveDeferredEnable = false;
- private long mDisabledTimestamp;
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(false);
- // Supplicant can't restart right away, so not the time we switched off
- mDisabledTimestamp = SystemClock.elapsedRealtime();
- mDeferredEnableSerialNumber++;
- mHaveDeferredEnable = false;
- }
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_WIFI_TOGGLED:
- case CMD_AIRPLANE_TOGGLED:
- if (mSettingsStore.isWifiToggleEnabled()) {
- if (doDeferEnable(msg)) {
- if (mHaveDeferredEnable) {
- // have 2 toggles now, inc serial number an ignore both
- mDeferredEnableSerialNumber++;
- }
- mHaveDeferredEnable = !mHaveDeferredEnable;
- break;
- }
- if (mDeviceIdle == false) {
- transitionTo(mDeviceActiveState);
- } else {
- checkLocksAndTransitionWhenDeviceIdle();
- }
- }
- break;
WifiController的ApStaDisabledState的处理很简单,只是简单的transition到DeviceActiveState,因为DeviceActiveState的父State是StaEnabledState,由StateMachine的知识,我们先到StaEnabledState和DeviceActiveState的enter()函数来看看:
- class StaEnabledState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(true);
- }
- class DeviceActiveState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
- mWifiStateMachine.setHighPerfModeEnabled(false);
- }
上面分别是调用WifiStateMachine的四个函数,这四个函数都是给WifiStateMachine发送四个消息,分别是CMD_START_SUPPLICANT、SET_OPERATIONAL_MODE、CMD_START_DRIVER和SET_HTGH_PERF_MODE,如上图中的Figure 1所示。接着我们到WifiStateMachine的InitialState中去看看如何处理CMD_START_SUPPLICANT:
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_SUPPLICANT:
- if (mWifiNative.loadDriver()) {
- try {
- mNwService.wifiFirmwareReload(mInterfaceName, "STA");
- } catch (Exception e) {
- loge("Failed to reload STA firmware " + e);
- // continue
- }
- try {
- mNwService.disableIpv6(mInterfaceName);
- } catch (RemoteException re) {
- loge("Unable to change interface settings: " + re);
- } catch (IllegalStateException ie) {
- loge("Unable to change interface settings: " + ie);
- }
- mWifiMonitor.killSupplicant(mP2pSupported);
- if(mWifiNative.startSupplicant(mP2pSupported)) {
- setWifiState(WIFI_STATE_ENABLING);
- if (DBG) log("Supplicant start successful");
- mWifiMonitor.startMonitoring();
- transitionTo(mSupplicantStartingState);
- } else {
- loge("Failed to start supplicant!");
- }
- } else {
- loge("Failed to load driver");
- }
- break;
这里主要调用WifiNative的loadDriver和startSupplicant两个函数去加载wifi driver和启动wpa_supplicant,当启动成功wpa_supplicant后,就会调用WifiMonitor的startMonitoring去和wpa_supplicant建立socket连接,并不断的从wpa_supplicant收event。wpa_supplicant是一个独立的运行程序,它和应用程序之间通过socket来通信,主要存在两个socket连接,一个用来向wpa_supplicant发送命令,另一个是wpa_supplicant用来向应用程序通知event,应用程序在收到event后可以知道当前的连接状态来进行下一步动作。我们进入到WifiMonitor.startMonitoring这个函数看看:
- public synchronized void startMonitoring(String iface) {
- WifiMonitor m = mIfaceMap.get(iface);
- if (m == null) {
- Log.e(TAG, "startMonitor called with unknown iface=" + iface);
- return;
- }
- Log.d(TAG, "startMonitoring(" + iface + ") with mConnected = " + mConnected);
- if (mConnected) {
- m.mMonitoring = true;
- m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT);
- } else {
- if (DBG) Log.d(TAG, "connecting to supplicant");
- int connectTries = 0;
- while (true) {
- if (mWifiNative.connectToSupplicant()) {
- m.mMonitoring = true;
- m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT);
- new MonitorThread(mWifiNative, this).start();
- mConnected = true;
- break;
- }
- if (connectTries++ < 5) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ignore) {
- }
- } else {
- mIfaceMap.remove(iface);
- m.mWifiStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
- Log.e(TAG, "startMonitoring(" + iface + ") failed!");
- break;
- }
- }
- }
- }
这个方法里面主要调用WifiNative的connenctToSupplicant去和wpa_supplicant建立socket连接,然后给WifiStateMachine发送一个SUP_CONNECTION_EVENT消息,最后新建一个MonitorThread运行,MonitorThread就是一个循环,不断的从wpa_supplicant收event,然后进行解析,并dispatch到不同的函数去处理,后面我们再来分析MonitorThread的流程。回到WifiStateMachine的InitialState中去看看如何处理CMD_START_SUPPLICANT的流程中来,当startMonitoring结束后,WifiStateMachine就跳转到SupplicantStartingState,这时的WifiStateMachine和MessageQueue里面的消息队列上图中的Figure 2。
接着来看当WifiStateMachine处理完SUP_CONNECTION_EVENT消息后,马上会收到SET_OPERATIONAL_MODE和CMD_START_DRIVER消息,这两个消息都会被SupplicantStartingState延迟处理,SET_HTGH_PERF_MODE会被DefaultState处理。接着SupplicantStartingState会收到SUP_CONNECTION_EVENT,处理代码如下:
- public boolean processMessage(Message message) {
- switch(message.what) {
- case WifiMonitor.SUP_CONNECTION_EVENT:
- if (DBG) log("Supplicant connection established");
- setWifiState(WIFI_STATE_ENABLED);
- mSupplicantRestartCount = 0;
- /* Reset the supplicant state to indicate the supplicant
- * state is not known at this time */
- mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- /* Initialize data structures */
- mLastBssid = null;
- mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
- mLastSignalLevel = -1;
- mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
- mWifiConfigStore.loadAndEnableAllNetworks();
- initializeWpsDetails();
- sendSupplicantConnectionChangedBroadcast(true);
- transitionTo(mDriverStartedState);
- break;
在SUP_CONNECTION_EVENT的处理流程中,主要是调用WifiConfigStore的loadAndEnableAllNetworks函数来加载并enable用户之前连接过并保存的AP,然后会初始化一些Wps相关的信息,最后transition到DriverStartedState上,如上图的Figure 3。再来看DriverStartedState的enter函数,这里面有一些重要的流程:
- class DriverStartedState extends State {
- @Override
- public void enter() {
- /* set country code */
- setCountryCode();
- /* set frequency band of operation */
- setFrequencyBand();
- /* initialize network state */
- setNetworkDetailedState(DetailedState.DISCONNECTED);
- mDhcpActive = false;
- startBatchedScan();
- if (mOperationalMode != CONNECT_MODE) {
- mWifiNative.disconnect();
- mWifiConfigStore.disableAllNetworks();
- if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
- setWifiState(WIFI_STATE_DISABLED);
- }
- transitionTo(mScanModeState);
- } else {
- /* Driver stop may have disabled networks, enable right after start */
- mWifiConfigStore.enableAllNetworks();
- if (DBG) log("Attempting to reconnect to wifi network ..");
- mWifiNative.reconnect();
- // Status pulls in the current supplicant state and network connection state
- // events over the monitor connection. This helps framework sync up with
- // current supplicant state
- mWifiNative.status();
- transitionTo(mDisconnectedState);
- }
- if (mP2pSupported) {
- if (mOperationalMode == CONNECT_MODE) {
- mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
- } else {
- // P2P statemachine starts in disabled state, and is not enabled until
- // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to
- // keep it disabled.
- }
- }
- }
enter函数的代码比较多,上面是精简后的代码,上面主要分为两条分支,一是OperationalMode != CONNECT_MODE,一种是OperationalMode = CONNECT_MODE,根据官方的解释,OperationalMode一共有三种,分别如下:
1.CONNECT_MODE,这种模式下,STA可以scan并连接热点
2.SCAN_ONLY_MODE,这种模式下,STA只能扫描热点
3.SCAN_ONLY_WIFI_OFF_MODE,在这种模式下,当wifi是toggle off的情况下,也可以进行scan
这三种模式默认的是CONNECT_MODE,后面两种模式现在用到的不多,但按照Google的设计,后面可能会有很多的app会用到,比如利用热点来进行点位,这个应用其实这需要能够scan,并不需要链接热点。那我们接看这OperationalMode = CONNECT_MODE的流程,它直接transition 到DisconnectedState,如上图中的Figure 4,在transition到DisconnectedState之前,还会向WifiNative下reconnect的命令,用于重新连接上次连接但没有forget的AP,即开机后自动连上AP。如平台支持P2P,这里还将会给WifiP2pService发送CMD_ENABLE_P2P的消息,以后在学习P2P模块的时候再来分析。
到这里,Wifi toggle on的流程就分析完了。
WiFi toggled off流程分析
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_WIFI_TOGGLED:
- if (! mSettingsStore.isWifiToggleEnabled()) {
- if (mSettingsStore.isScanAlwaysAvailable()) {
- transitionTo(mStaDisabledWithScanState);
- } else {
- transitionTo(mApStaDisabledState);
- }
- }
- break;
WifiController会transition 到ApStaDisabledState中,这样WifiController这个状态机就回到的最开始的初始状态了,进到ApStaDisabledState的enter函数分析如何对WifiStateMachine这个状态机做处理:
- class ApStaDisabledState extends State {
- private int mDeferredEnableSerialNumber = 0;
- private boolean mHaveDeferredEnable = false;
- private long mDisabledTimestamp;
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(false);
- // Supplicant can't restart right away, so not the time we switched off
- mDisabledTimestamp = SystemClock.elapsedRealtime();
- mDeferredEnableSerialNumber++;
- mHaveDeferredEnable = false;
- }
这里主要调用WifiStateMachine的setSupplicantRunning(false),这个函数直接给WifiStateMachine发送一个CMD_STOP_SUPPLICAN消息,由前面的知识,我们知道SupplicantStartedState会处理这个消息,进到具体代码中分析:
- public boolean processMessage(Message message) {
- switch(message.what) {
- case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
- if (mP2pSupported) {
- transitionTo(mWaitForP2pDisableState);
- } else {
- transitionTo(mSupplicantStoppingState);
- }
- break;
如果平台支持P2P,这里会跳转到WaitForP2pDisableState中,如果不支持则会跳转到SupplicantStoppingState中。这里我们看mP2pSupported为true的情况,因为现在大多数平台都应该支持P2P了,另外,在WaitForP2pDisableState中处理完P2P相关的内容后,也会跳转到SupplicantStoppingState中来。我们来分析WaitForP2pDisableState的enter函数:
- class WaitForP2pDisableState extends State {
- private State mTransitionToState;
- @Override
- public void enter() {
- switch (getCurrentMessage().what) {
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- mTransitionToState = mInitialState;
- break;
- case CMD_DELAYED_STOP_DRIVER:
- mTransitionToState = mDriverStoppingState;
- break;
- case CMD_STOP_SUPPLICANT:
- mTransitionToState = mSupplicantStoppingState;
- break;
- default:
- mTransitionToState = mDriverStoppingState;
- break;
- }
- mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- case WifiStateMachine.CMD_DISABLE_P2P_RSP:
- transitionTo(mTransitionToState);
- break;
- class SupplicantStoppingState extends State {
- @Override
- public void enter() {
- /* Send any reset commands to supplicant before shutting it down */
- handleNetworkDisconnect();
- if (mDhcpStateMachine != null) {
- mDhcpStateMachine.doQuit();
- }
- if (DBG) log("stopping supplicant");
- mWifiMonitor.stopSupplicant();
- /* Send ourselves a delayed message to indicate failure after a wait time */
- sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
- ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
- setWifiState(WIFI_STATE_DISABLING);
- mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- case WifiMonitor.SUP_CONNECTION_EVENT:
- loge("Supplicant connection received while stopping");
- break;
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- if (DBG) log("Supplicant connection lost");
- handleSupplicantConnectionLoss();
- transitionTo(mInitialState);
- break;
- case CMD_STOP_SUPPLICANT_FAILED:
- if (message.arg1 == mSupplicantStopFailureToken) {
- loge("Timed out on a supplicant stop, kill and proceed");
- handleSupplicantConnectionLoss();
- transitionTo(mInitialState);
- }
- break;
在SupplicantStoppingState主要调用handleNetworkDisconnect和stopSupplicant函数,handleNetworkDisconnect主要工作是停掉stopDhcp和clear一些状态信息;WifiMonitor的stopSupplicant用于停掉wpa_supplicant,就是向wpa_supplicant发送一个TERMINATE命令,当wpa_supplicant收到TERMINATE命令会,就会给调用者发送CTRL-EVENT-TERMINATING这个event,当WifiMonitor收到这个event后,又会给WifiStateMachine发送SUP_DISCONNECTION_EVENT消息。回到SupplicantStoppingState收到这个消息后,就可以去结束掉wpa_supplicant进程并断开与它的socket连接,并且transition到InitialState,这样WifiStateMachine也恢复到最初的状态了。另外,从上面enter函数可以看到,还会发送一个CMD_STOP_SUPPLICANT_FAILED消息给自己,如果调用stopSupplicant不成功,间隔SUPPLICANT_RESTART_INTERVAL_MSECS毫秒后,也会走到和SUP_DISCONNECTION_EVENT消息处理一样的流程中来。