Android -- Wifi启动流程分析
16lz
2021-01-23
Android -- Wifi启动流程分析
Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了;下面就简单介绍下Android中Wifi的启动流程。
当我在Setting菜单里点击打开Wifi时,调用的入口函数是WifiManager::setWifiEnabled(boolean enabled):
/** * Enable or disable Wi-Fi. * @param enabled {@code true} to enable, {@code false} to disable. * @return {@code true} if the operation succeeds (or if the existing state * is the same as the requested state). */ public boolean setWifiEnabled(boolean enabled) { try { return mService.setWifiEnabled(enabled); } catch (RemoteException e) { return false; } }
通过AIDL方式,在Android6.0中,实际调用的是WifiServiceImpl::setWifiEnabled(boolean enable): /** * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)} * @param enable {@code true} to enable, {@code false} to disable. * @return {@code true} if the enable/disable operation was * started or is already in the queue. */ 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; }
从代码可以看出,这里主要的操作是将wifi是否enable的状态存入数据库、向WiFiController发送了CMD_WIFI_TOGGLED消息。 WifiController实际上是一个状态机,相比WifiStateMachine,它的状态较少,结构也比较简单。WifiController的定义及构造函数:
class WifiController extends StateMachine { ... WifiController(Context context, WifiServiceImpl service, Looper looper) { super(TAG, looper); ... addState(mDefaultState); addState(mApStaDisabledState, mDefaultState); addState(mStaEnabledState, mDefaultState); addState(mDeviceActiveState, mStaEnabledState); addState(mDeviceInactiveState, mStaEnabledState); addState(mScanOnlyLockHeldState, mDeviceInactiveState); addState(mFullLockHeldState, mDeviceInactiveState); addState(mFullHighPerfLockHeldState, mDeviceInactiveState); addState(mNoLockHeldState, mDeviceInactiveState); addState(mStaDisabledWithScanState, mDefaultState); addState(mApEnabledState, mDefaultState); addState(mEcmState, mDefaultState); boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn(); boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled(); boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable(); log("isAirplaneModeOn = " + isAirplaneModeOn + ", isWifiEnabled = " + isWifiEnabled + ", isScanningAvailable = " + isScanningAlwaysAvailable); if (isScanningAlwaysAvailable) { setInitialState(mStaDisabledWithScanState); } else { setInitialState(mApStaDisabledState); } ... } ...}
WifiController状态机的创建、开启工作在WifiServiceImpl中完成: public WifiServiceImpl(Context context) { mContext = context; mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName); mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller); mWifiStateMachine.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); mPowerManager = context.getSystemService(PowerManager.class); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); mUserManager = UserManager.get(mContext); mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine); mSettingsStore = new WifiSettingsStore(mContext); HandlerThread wifiThread = new HandlerThread("WifiService"); wifiThread.start(); mClientHandler = new ClientHandler(wifiThread.getLooper()); mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); mWifiController = new WifiController(mContext, this, wifiThread.getLooper()); }
WifiController中的各状态之间的关系如图: WifiControlle状态机的初始状态由一些配置信息决定。当ApStaDisabledState为初始状态时,看对CMD_WIFI_TOGGLED消息的处理:
class ApStaDisabledState extends State { ... @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(); } } else if (mSettingsStore.isScanAlwaysAvailable()) { transitionTo(mStaDisabledWithScanState); } break; case CMD_SCAN_ALWAYS_MODE_CHANGED: if (mSettingsStore.isScanAlwaysAvailable()) { transitionTo(mStaDisabledWithScanState); } break; ... default: return NOT_HANDLED; } return HANDLED; } private boolean doDeferEnable(Message msg) { long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp; if (delaySoFar >= mReEnableDelayMillis) { return false; } log("WifiController msg " + msg + " deferred for " + (mReEnableDelayMillis - delaySoFar) + "ms"); // need to defer this action. Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE); deferredMsg.obj = Message.obtain(msg); deferredMsg.arg1 = ++mDeferredEnableSerialNumber; sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS); return true; } }
调试过程中发现,ApStaDisabledState会同时忽略两个时间间隔小于500ms的 CMD_WIFI_TOGGLED消息,接着转换到DeviceActiveState状态。StaEnabledState是它的父状态,由StateMachine的知识可知,转换到该状态时,会依次调用父、子状态的enter()函数。我们看两个状态的enter()函数: class StaEnabledState extends State { @Override public void enter() { mWifiStateMachine.setSupplicantRunning(true); } ... } } /* Parent: StaEnabledState */ class DeviceActiveState extends State { @Override public void enter() { mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); mWifiStateMachine.setDriverStart(true); mWifiStateMachine.setHighPerfModeEnabled(false);//此处分析忽略,关系不大 } ... }
这里依次会向WifiStateMachine发送三个消息,最后一个消息这里忽略: - WifiStateMachine.setSupplicantRunning(true):发送CMD_START_SUPPLICANT消息
- WifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE):发送CMD_SET_OPERATIONAL_MODE消息,参数是CONNECT_MODE
- WifiStateMachine.setDriverStart(true):发送CMD_START_DRIVER消息
在此之前,我们先看下第二点中的参数CONNECT_MODE的含义。在WifiStateMachine中,已经有了如下定义:
/* Wifi state machine modes of operation */ /* CONNECT_MODE - connect to any 'known' AP when it becomes available */ public static final int CONNECT_MODE = 1; /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */ public static final int SCAN_ONLY_MODE = 2; /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */ public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
/* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE * In CONNECT_MODE, the STA can scan and connect to an access point * In SCAN_ONLY_MODE, the STA can only scan for access points * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off */ private int mOperationalMode = CONNECT_MODE;
可知Wifi状态机一共有三种处理模式: - CONNECT_MODE:该状态下Wifi可以扫描AP,也可以连接AP
- SCAN_ONLY_MODE:该状态下Wifi尽可以扫描AP
- SCAN_ONLY_WIFI_OFF_MODE:该状态下,Wifi仅可以当Wifi toogle off时允许扫描AP
现在,我们将处理的过程转换到WifiStateMachine。WifiStateMachine是一个复杂的状态机,它维护了Wifi的启动、扫描、连接、断开等多个状态。它运行在自己独有的线程中,拥有自己的消息队列。WifiStateMachine中各状态的关系如图所示:
分别来看WifiStateMachine是怎么处理这三个消息的。InitialState首先接收到CMD_START_SUPPLICANT消息并处理:
case CMD_START_SUPPLICANT: if (mWifiNative.loadDriver()) {//加载驱动 try { mNwService.wifiFirmwareReload(mInterfaceName, "STA");//加载wlan固件 } catch (Exception e) { loge("Failed to reload STA firmware " + e); // Continue } try { // A runtime crash can leave the interface up and // IP addresses configured, and this affects // connectivity when supplicant starts up. // Ensure interface is down and we have no IP // addresses before a supplicant start. mNwService.setInterfaceDown(mInterfaceName); mNwService.clearInterfaceAddresses(mInterfaceName); // Set privacy extensions mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); // IPv6 is enabled only as long as access point is connected since: // - IPv6 addresses and routes stick around after disconnection // - kernel is unaware when connected and fails to start IPv6 negotiation // - kernel can start autoconfiguration when 802.1x is not complete mNwService.disableIpv6(mInterfaceName); } catch (RemoteException re) { loge("Unable to change interface settings: " + re); } catch (IllegalStateException ie) { loge("Unable to change interface settings: " + ie); } /* Stop a running supplicant after a runtime restart * Avoids issues with drivers that do not handle interface down * on a running supplicant properly. */ mWifiMonitor.killSupplicant(mP2pSupported); if (WifiNative.startHal() == false) { /* starting HAL is optional */ loge("Failed to start HAL"); } if (mWifiNative.startSupplicant(mP2pSupported)) {//启动wpa_s setWifiState(WIFI_STATE_ENABLING); if (DBG) log("Supplicant start successful"); mWifiMonitor.startMonitoring();//建立与wpa_s之间的socket通信连接;开启线程,循环接收来自wpa_s的event,并分发处理 transitionTo(mSupplicantStartingState); } else { loge("Failed to start supplicant!"); } } else { loge("Failed to load driver"); } break;
主要的处理过程包括: - WifiNative.loadDriver():加载Wifi驱动,实际的实现是在wifi.c中
- NetworkManagementService.wifiFirmwareReload(mInterfaceName, "STA"):加载wlan固件
- WifiNative.startSupplicant(mP2pSupported):启动wpa_supplicant
- WifiMonitor.startMonitoring():在前面一篇博文中已经介绍过WifiMonitor,这一步主要是在WifiMonitor中建立与wpa_supplicant通信的socket通道、创建一个线程接收底层事件并分发处理。这里会创建两个socket通道与wpa_s通信,一个用于下发指令,另一个用于接收事件。成功后WifiMonitor会向WifiStateMachine发送一个代表socket通信建立成功的消息:SUP_CONNECTION_EVENT;收到这个消息就表示Wifi已经启动成功了。
- 切换到SupplicantStartingState。
进入SupplicantStartingState后,第一个消息就处理完毕,这时消息队列中按处理先后顺序仍有三个消息:
- CMD_SET_OPERATIONAL_MODE,参数是CONNECT_MODE
- CMD_START_DRIVER
- SUP_CONNECTION_EVENT
切换到SupplicantStartingState,消息队列中的前两个消息在该状态会被延迟处理,直接看SUP_CONNECTION_EVENT的处理过程:
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()); /* set frequency band of operation */ setFrequencyBand(); mWifiNative.enableSaveConfig(); mWifiConfigStore.loadAndEnableAllNetworks();//加载并enable保存的AP if (mWifiConfigStore.enableVerboseLogging.get() > 0) { enableVerboseLogging(mWifiConfigStore.enableVerboseLogging.get()); } initializeWpsDetails(); sendSupplicantConnectionChangedBroadcast(true);//广播通知Wpa_s连接已建立,此时已经可以准备连接或扫描Wifi了 transitionTo(mDriverStartedState); break;
这里主要是调用WifiConfigStore.loadAndEnableAllNetworks()加载并enable所有保存在wpa_s中的AP,然后做一些其他的初始化工作,切换到DriverStartedState状态。关注其父状态和自身的enter()函数: class SupplicantStartedState extends State { @Override public void enter() { /* Wifi is available as long as we have a connection to supplicant */ mNetworkInfo.setIsAvailable(true); if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo); 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); mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);//设置扫描时间间隔 mWifiNative.setExternalSim(true); /* turn on use of DFS channels */ WifiNative.setDfsFlag(true); /* set country code */ setCountryCode(); setRandomMacOui(); mWifiNative.enableAutoConnect(false); //可以事先注意该设置 }} class DriverStartedState extends State { @Override public void enter() { if (PDBG) { logd("DriverStartedState enter"); } mWifiLogger.startLogging(mVerboseLoggingLevel > 0); mIsRunning = true; mInDelayedStop = false; mDelayedStopCounter++; updateBatteryWorkSource(null); /** * Enable bluetooth coexistence scan mode when bluetooth connection is active. * When this mode is on, some of the low-level scan parameters used by the * driver are changed to reduce interference with bluetooth */ mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive); /* initialize network state */ setNetworkDetailedState(DetailedState.DISCONNECTED); /* Remove any filtering on Multicast v6 at start */ mWifiNative.stopFilteringMulticastV6Packets(); /* Reset Multicast v4 filtering state */ if (mFilteringMulticastV4Packets.get()) { mWifiNative.startFilteringMulticastV4Packets(); } else { mWifiNative.stopFilteringMulticastV4Packets(); } mDhcpActive = false; if (mOperationalMode != CONNECT_MODE) { mWifiNative.disconnect(); mWifiConfigStore.disableAllNetworks(); if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) { setWifiState(WIFI_STATE_DISABLED); } transitionTo(mScanModeState); } else { // 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 // TODO: actually check th supplicant status string and make sure the supplicant // is in disconnecte4d state. mWifiNative.status(); // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin transitionTo(mDisconnectedState); transitionTo(mDisconnectedState); } // We may have missed screen update at boot if (mScreenBroadcastReceived.get() == false) { PowerManager powerManager = (PowerManager)mContext.getSystemService( Context.POWER_SERVICE); handleScreenStateChanged(powerManager.isScreenOn()); } else { // Set the right suspend mode settings mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0 && mUserWantsSuspendOpt.get()); } mWifiNative.setPowerSave(true); if (mP2pSupported) { if (mOperationalMode == CONNECT_MODE) { mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);//支持p2p,则会发送命令enbale 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. } } final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); mHalFeatureSet = WifiNative.getSupportedFeatureSet(); if ((mHalFeatureSet & WifiManager.WIFI_FEATURE_HAL_EPNO) == WifiManager.WIFI_FEATURE_HAL_EPNO) { mHalBasedPnoDriverSupported = true; } // Enable link layer stats gathering mWifiNative.setWifiLinkLayerStats("wlan0", 1); if (PDBG) { logd("Driverstarted State enter done, epno=" + mHalBasedPnoDriverSupported + " feature=" + mHalFeatureSet); } }}
SupplicantStartedState的enter()函数设置了Wifi扫描间隔;DriverStartedState的enter()函数主要进行了一些相关的设置工作,根据配置启动p2p。最后在enter()函数中会将状态切换到DisconnectedState。消息队列中被延迟的两条消息此时会被处理: - CMD_SET_OPERATIONAL_MODE消息在DisconnectedState被处理,将mOperationalMode设置为CONNECT_MODE;Wifi状态机中该字段的默认值也是该值。
- CMD_START_DRIVER消息则在DriverStartedState中被处理。
最后转换到DisconnectedState状态,关注enter()函数:
public void enter() { // We dont scan frequently if this is a temporary disconnect // due to p2p if (mTemporarilyDisconnectWifi) { mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE); return; } if (PDBG) { logd(" Enter DisconnectedState scan interval " + mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get() + " mLegacyPnoEnabled= " + mLegacyPnoEnabled + " screenOn=" + mScreenOn + " useGscan=" + mHalBasedPnoDriverSupported + "/" + mWifiConfigStore.enableHalBasedPno.get()); } /** clear the roaming state, if we were roaming, we failed */ mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE; if (useHalBasedAutoJoinOffload()) { startGScanDisconnectedModeOffload("disconnectedEnter"); } else { if (mScreenOn) { /** * screen lit and => delayed timer */ startDelayedScan(500, null, null); } else { /** * screen dark and PNO supported => scan alarm disabled */ if (mBackgroundScanSupported) { /* If a regular scan result is pending, do not initiate background * scan until the scan results are returned. This is needed because * initiating a background scan will cancel the regular scan and * scan results will not be returned until background scanning is * cleared */ if (!mIsScanOngoing) { enableBackgroundScan(true);//启动wifi扫描,随后会触发autojoin,进行连接操作 } } else { setScanAlarm(true);//启动一个扫描定时器 } } } /** * If we have no networks saved, the supplicant stops doing the periodic scan. * The scans are useful to notify the user of the presence of an open network. * Note that these are not wake up scans. */ if (mNoNetworksPeriodicScan != 0 && !mP2pConnected.get() && mWifiConfigStore.getConfiguredNetworks().size() == 0) { sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan); } mDisconnectedTimeStamp = System.currentTimeMillis(); mDisconnectedPnoAlarmCount = 0; }
在enter()函数中,会进行scan动作;WifiStateMachine处理SCAN_RESULTS_EVENT消息时,就会进入autojoin流程,尝试AP重连。 PS:流程图下载: http://download.csdn.net/detail/csdn_of_coder/9702484
更多相关文章
- Android 页面自动跳转方法(比如进入app的广告,通过Timer计时器,通过
- android 如何在状态栏上增加一个icon
- Rexsee API介绍:Android屏幕锁定,Keyguard函数与扩展源码
- Android基本控件和事件以及消息总结
- Android录制或播放语音消息时关闭其他媒体播放
- Android SharedPreferences保存登录状态
- Android 平台上,界面元素在定时器的响应函数里刷新。