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