最近在解決wifi的一些問題,故研究下wifi源碼。
該源碼是基于Android4.3,其他版本略有改動,大致流程一樣。
這篇主要說一下wifi的啟動流程。
WifiManager
先從wifi的開啟來看,WifiManager中提供了接口用來控制wifi開關,setWifiEnabled,參數true表示開啟、false表示關閉。
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(enabled);
} catch (RemoteException e) {
return false;
}
}
該方法調用到WifiService中的setWifiEnabled。
WifiService
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;
}
enforceChangePermission 判斷調用的進程是否有權限。想要開關wifi需要CHANGE_WIFI_STATE 權限。
handleWifiToggled 判斷飛行模式、保存wifi 操作的狀態。
向WifiController發送消息。CMD_WIFI_TOGGLED
WifiController
WifiController在WIfiService的構造函數中初始化、并開始運行。
WifiController繼承了StateMachine,是一個狀態機。其構造函數如下
WifiController(Context context, WifiService 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);
if (mSettingsStore.isScanAlwaysAvailable()) {
setInitialState(mApStaDisabledState);
} else {
setInitialState(mApStaDisabledState);
}
}
上述函數中addState的格式,可以看出各狀態之間的關系。
然后通過wifi是否可以一直掃描(isScanAlwaysAvailable)設置狀態機初始狀態。
ApStaDisabledState和StaDisabledWithScanState兩種狀態處理CMD_WIFI_TOGGLED消息時一樣的。
看看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;
//。。。。。
}
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;
}
}
mSettingsStore.isWifiToggleEnabled()用來獲取保存的wifi操作的狀態,如果是開則繼續。
wpa supplicant 關閉后不能立即啟動,mReEnableDelayMillis為重新開啟wifi的延時時間(從系統數據庫獲取,獲取不到則默認值為500ms),這里deferEnable用來判斷時間,延緩啟動。
之后切換到DeviceActiveState。
DeviceActiveState為StaEnabledState的子狀態,所以會先調用父狀態的enter()函數,然后調用子狀態的enter()函數。分別看一下兩個enter函數。
1 WifiController --StaEnabledState
先看看StaEnabledState的enter()函數:
class StaEnabledState extends State {
@Override
public void enter() {
mWifiStateMachine.setSupplicantRunning(true);
}
...
}
}
mWifiStateMachine.setSupplicantRunning(true),WifiStateMachine發送Message--CMD_STAET_SUPPLICANT。
WIfiStateMachine
看看處理該消息
class InitialState extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START_SUPPLICANT:
//加載Wifi驅動
if (mWifiNative.loadDriver()) {
try {//加載固件
mNwService.wifiFirmwareReload(mInterfaceName, "STA");
} catch (Exception e) {
}
try {
// A runtime crash can leave the interface up and
// this affects connectivity when supplicant starts up.
// Ensure interface is down before a supplicant start.
mNwService.setInterfaceDown(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.
*/
mWifiNative.killSupplicant(mP2pSupported);
//開啟wpa_supplicant
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():加載Wifi驅動。
WifiNative.loadDriver()-->android_net_wifi_wifi.cpp(android_net_wifi_loadDriver)
->wifi.c(wifi_load_driver)。mNwService.wifiFirmwareReload(mInterfaceName, "STA"); 加載固件
mWifiNative.startSupplicant(mP2pSupported) 先保證沒有運行的wpa supplicant,然后開啟wpa supplicant,其中參數mP2pSupported表示是否支持wifi 直連。
WifiNative.startSupplicant()-->android_net_wifi_wifi.cpp(android_net_wifi_startSupplicant)
->wifi.c(wifi_start_supplicant)。setWifiState(WIFI_STATE_ENABLING) 發送廣播(wifi狀態改變 ,正在開啟)。
mWifiMonitor.startMonitoring()
WifiMonitor創建一個線程MonitorThrad
- connectToSupplicant最終調到wifi.c 中的wifi_connect_on_socket_path。在該函數中,將通過wpa_ctrl_open函數分別創建兩個socket,一個是ctrl_conn, 用于向wpa_supplicant發送命令并接收response, 另一個是monitor_conn, 它一直阻塞等待從wpa_supplicant過來的event。連接成功后WifiMonitor會向WifiStateMachine發送一個代表socket通信建立成功的消息:SUP_CONNECTION_EVENT。
- mWifiNative.waitForEvent-》wifi_wait_on_socket ,循環調用該函數,接收底層事件并分發處理。
- WifiStateMachine切換到SupplicantStartingState狀態。步驟5是在另一個線程中運行,并且是耗時操作,所以WifiStateMachine先切換到SupplicantStartingState狀態,然后接收到SUP_CONNECTION_EVENT消息。
下面看看如何處理SUP_CONNECTION_EVENT消息。
class SupplicantStartingState extends State {
@Override
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;
//......
}
setWifiState(WIFI_STATE_ENABLED),發送WIFI_STATE_ENABLED的廣播。
WifiConfigStore.loadAndEnableAllNetworks() 加載并enable所有保存在wpa_supplicant中的AP。這樣會使這些保存的wifi實現自動重連。
切換到DriverStartedState狀態。
DriverStartedState是SupplicantStartedState的子狀態,所以先后運行SupplicantStartedState、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);
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);
}
//.....
frameworks/base/core/res/res/values/config.xml中可以修改config_wifi_supplicant_scan_interval 的值。
mNetworkInfo.setIsAvailable(true); 連接到wpa_supplicant,就可以使用wifi。
setScanInterval 設置wifi掃描間隔時間。
class DriverStartedState extends State {
@Override
public void enter() {
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);
/* set country code */
setCountryCode();
/* set frequency band of operation */
setFrequencyBand();
/* 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();
}
if (mOperationalMode != CONNECT_MODE) {
mWifiNative.disconnect();
transitionTo(mScanModeState);
} else {
/* Driver stop may have disabled networks, enable right after start */
mWifiConfigStore.enableAllNetworks();
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);
}
// 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) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
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);
}
//.....
setBluetoothCoexistenceScanMode 啟用藍牙連接時啟用藍牙共存掃描模式。當此模式打開時,驅動程序使用的一些低級掃描參數會被更改,以減少對藍牙的干擾。
mOperationalMode 默認為CONNECT_MODE,enableAllNetworks 將保存的wifi置為可連接的,并進行重連。
mWifiNative.status() 將wpa_supplicant中狀態進行同步。
切換到DisconnectedState狀態。
2 WifiController --DeviceActiveState
/* Parent: StaEnabledState */
class DeviceActiveState extends State {
@Override
public void enter() {
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mWifiStateMachine.setDriverStart(true);
mWifiStateMachine.setHighPerfModeEnabled(false);
}
...
}
STA的3個操作狀態:CONNECT_MODE,SCAN_ONLY_MODE,SCAN_ONLY_WIFI_OFF_MODE
*在CONNECT_MODE中,STA可以掃描并連接到接入點
*在SCAN_ONLY_MODE中,STA只能掃描接入點
*在SCAN_ONLY_WIFI_OFF_MODE中,STA只能掃描(wifi關閉狀態下)
setOperationalMode 這里設置模式 可掃描、可連接。
setDriverStart(true) 發送CMD_START_DRIVER 消息。該消息在DriverStartedState中處理。
case CMD_START_DRIVER:
if (mInDelayedStop) {
mInDelayedStop = false;
mDelayedStopCounter++;
mAlarmManager.cancel(mDriverStopIntent);
if (DBG) log("Delayed stop ignored due to start");
if (mOperationalMode == CONNECT_MODE) {
mWifiConfigStore.enableAllNetworks();
}
}
break;
如果正在延時關閉驅動,則取消關閉。并將保存的wifi都Enable。
setHighPerfModeEnabled暫時不清楚什么情況。
這就差不多整理完wifi啟動的流程了。