Android N Wi-Fi 啟動流程分析
阿新 • • 發佈:2019-01-01
當我們在手機的setting中開啟wifi開關時,首先是wifisetting中的oncreate將載入佈局檔案。
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.wifi_settings);//載入檔案佈局
mAddPreference = new Preference(getContext());
mAddPreference.setIcon(R.drawable.ic_menu_add_inset);
mAddPreference.setTitle(R.string.wifi_add_network);
mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
mBgThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
mBgThread.start();
mWifiSettingsExt = new WifiSettingsExt(getActivity());
mWifiSettingsExt.onCreate();
}
接下來在onStart中將建立 WifiEnabler
@Override
public void onStart() {
super.onStart();
// On/off switch is hidden for Setup Wizard (returns null)
mWifiEnabler = createWifiEnabler();
}
/**
* @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
*/
/* package */ WifiEnabler createWifiEnabler() {
final SettingsActivity activity = (SettingsActivity) getActivity();
return new WifiEnabler(activity, activity.getSwitchBar());
}
WifiEnabler 建構函式中,將獲得 WifiManager,並且新增廣播監聽事件,接下來顯示 switchBar, 同時將註冊switchBar的監聽事件。
public WifiEnabler(Context context, SwitchBar switchBar) {
mContext = context;
mSwitchBar = switchBar;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
// The order matters! We really should not depend on this. :(
mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
setupSwitchBar();
}
public void setupSwitchBar() {
final int state = mWifiManager.getWifiState();
handleWifiStateChanged(state);
if (!mListeningToOnSwitchChange) {
mSwitchBar.addOnSwitchChangeListener(this);
mListeningToOnSwitchChange = true;
}
mSwitchBar.show();
}
當我們開啟wifi開關的時候,SwitchBar監聽事件將觸發。此時將執行mWifiManager.setWifiEnabled(isChecked)。
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
Log.d(TAG, "onCheckedChanged, isChecked = " + isChecked);
//Do nothing if called as a result of a state machine event
if (mStateMachineEvent) {
return;
}
// Show toast message if Wi-Fi is not allowed in airplane mode
if (isChecked && !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_WIFI)) {
Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
// Reset switch to off. No infinite check/listenenr loop.
mSwitchBar.setChecked(false);
return;
}
// Disable tethering if enabling Wifi
if (mayDisableTethering(isChecked)) {
mWifiManager.setWifiApEnabled(null, false);
}
Log.d(TAG, "onCheckedChanged, setWifiEnabled = " + isChecked);
MetricsLogger.action(mContext,
isChecked ? MetricsEvent.ACTION_WIFI_ON : MetricsEvent.ACTION_WIFI_OFF);
if (!mWifiManager.setWifiEnabled(isChecked)) {
// 開啟wifi開關
mSwitchBar.setEnabled(true);
Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}
}
WifiManager中將直接呼叫 WifiServiceImpl中的同名函式,此時WifiManager與WifiService將通過adil進行非同步通訊。
/**
* 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) {
throw e.rethrowFromSystemServer();
}
}
WifiServiceImpl 中的setWifiEnabled 將向 WifiController 中傳送 CMD_WIFI_TOGGLED 訊息。
/**
* 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();
/*
* 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;
}
//Added by [email protected] for defect 4640139 2017-06-09 begin
if (enable) {
if (mCm == null) {
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
mCm.stopTethering(ConnectivityManager.TETHERING_USB);
}
//Added by [email protected] for defect 4640139 2017-06-09 end
} finally {
Binder.restoreCallingIdentity(ident);
}
///M: put extra mWifiIpoOff
mWifiController.obtainMessage(CMD_WIFI_TOGGLED,
mWifiIpoOff ? WifiController.WIFI_TOGGLED_IPOOFF : 0,
Binder.getCallingUid()).sendToTarget();
return true;
}
WifiController也是一個狀態機,它將向 WifiStateMachine 中傳送三條訊息。分別是:
1. WifiStateMachine.setSupplicantRunning(true):傳送CMD_START_SUPPLICANT訊息
2. WifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE)
傳送CMD_SET_OPERATIONAL_MODE訊息,引數是CONNECT_MODE
3. WifiStateMachine.setDriverStart(true):傳送CMD_START_DRIVER訊息
接下來我們來到 WifiStateMachine 中,此時它已經完成了一些列初始化,並且處於 InitialState 狀態中,
InitialState 的processMessage 將處理第一條訊息 CMD_START_SUPPLICANT 。
case CMD_START_SUPPLICANT:
if (mWifiNative.loadDriver()) {//載入驅動
try {
mNwService.wifiFirmwareReload(mInterfaceName, "STA");
} catch (Exception e) {
loge("Failed to reload STA firmware " + e);
///M: ALPS02530654 Let wifi chip to recover @{
loge("fwreload fail, unloadDriver");
mWifiNative.unloadDriver();
///@}
///M: add the following @{
//throw exception to highlight wifi error
ExceptionLog exceptionLog = null;
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
return HANDLED;
}
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);
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 (mWifiNative.startHal() == false) {
/* starting HAL is optional */
loge("Failed to start HAL");
}
if (mWifiNative.startSupplicant(mP2pSupported)) {
setWifiState(WIFI_STATE_ENABLING);
if (DBG) log("Supplicant start successful");
mWifiMonitor.startMonitoring(mInterfaceName);
transitionTo(mSupplicantStartingState);
} else {
loge("Failed to start supplicant!");
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
///M: add the following @{
//throw exception to highlight wifi error
ExceptionLog exceptionLog = null;
}
} else {
loge("Failed to load driver");
///M: add the following @{
//throw exception to highlight wifi error
ExceptionLog exceptionLog = null;
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
}
break;
這裡主要是載入wifi的驅動,以及 WifiNative.startSupplicant 連線wpas,連線成功後,
函式setWifiState(WIFI_STATE_ENABLING)將傳送 WifiManager.WIFI_STATE_CHANGED_ACTION 廣播;
然後啟動startMonitoring(mInterfaceName),WifiMonitor與wpas建立聯絡後,將傳送SUP_CONNECTION_EVENT訊息給
WifiStateMachine
最後將狀態切換至 SupplicantStartingState 狀態。
此時 WifiStateMachine 將有三個訊息等待處理,他們分別是:
- CMD_SET_OPERATIONAL_MODE
- CMD_START_DRIVER
- SUP_CONNECTION_EVENT
切換至 SupplicantStartingState 狀態,將執行processMessage函式。
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
case CMD_SET_FREQUENCY_BAND:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
case CMD_SET_OPERATIONAL_MODE:
mOperationalMode = message.arg1;
deferMessage(message);
break;
可以發現,此時前兩條訊息都被延遲處理了,接下來看第三條訊息。
case WifiMonitor.SUP_CONNECTION_EVENT:
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();
mWifiConfigManager.loadAndEnableAllNetworks();
if (mWifiConfigManager.mEnableVerboseLogging.get() > 0) {
enableVerboseLogging(mWifiConfigManager.mEnableVerboseLogging.get());
}
initializeWpsDetails();
///M: scan stratagy
mConnectNetwork = false;
///M: For PPPOE
mLastExplicitNetworkId = INVALID_NETWORK_ID;
mOnlineStartTime = 0;
mUsingPppoe = false;
///M: for operator plugin @{
mConnectNetwork = false;
if (hasCustomizedAutoConnect()) {
mWifiNative.setBssExpireAge(IWifiFwkExt.BSS_EXPIRE_AGE);
mWifiNative.setBssExpireCount(IWifiFwkExt.BSS_EXPIRE_COUNT);
mDisconnectOperation = false;
mScanForWeakSignal = false;
mShowReselectDialog = false;
mIpConfigLost = false;
mLastCheckWeakSignalTime = 0;
if (!mWifiFwkExt.shouldAutoConnect()) {
disableAllNetworks(false);
}
}
///@}
///M: ALPS02317999 Prevent supplicant auto connect EAP SIM/AKA @{
if (isAirplaneModeOn()) {
List<WifiConfiguration> networks = mWifiConfigManager.getSavedNetworks();
if (null != networks) {
for (WifiConfiguration network : networks) {
if (mWifiConfigManager.isSimConfig(network)) {
mWifiConfigManager.disableNetwork(network.networkId);
///M: ALPS01975084 airplane mode on,
/// should not auto connect EAP SIM
WifiConfiguration eapSimConfig =
mWifiConfigManager.getWifiConfiguration(network.networkId);
///M:PPR2 igmore passpoint network
//because sim info may not get from sim.
if (eapSimConfig.isPasspoint() && mPPR2Support) {
} else {
mWifiConfigManager.updateNetworkSelectionStatus(
eapSimConfig,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_SIM_CARD_ABSENT);
}
}
}
} else {
log("Check for EAP_SIM_AKA, networks is null!");
}
///M: ALPS02319256 Disable EAP SIM/AKA AP if modem is not ready
} else if (!mSim1IccState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)
|| !mSim2IccState.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) {
log("iccState: (" + mSim1IccState + "," + mSim2IccState
+ "), check EAP SIM/AKA networks");
List<WifiConfiguration> networks = mWifiConfigManager.getSavedNetworks();
if (null != networks) {
for (WifiConfiguration network : networks) {
if (mWifiConfigManager.isSimConfig(network)
&& !isConfigSimCardLoaded(network)) {
log("diable EAP SIM/AKA network let supplicant cannot "
+ "auto connect, netId: " + network.networkId);
mWifiConfigManager.disableNetwork(network.networkId);
///M: ALPS02319256 airplane mode off and wifi on
// modem is not ready, supplicant should not auto connect
WifiConfiguration eapSimConfig =
mWifiConfigManager.getWifiConfiguration(network.networkId);
///M:PPR2 igmore passpoint network
//because sim info may not get from sim.
if (eapSimConfig.isPasspoint() && mPPR2Support) {
} else {
mWifiConfigManager.updateNetworkSelectionStatus(
eapSimConfig,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_SIM_CARD_ABSENT);
}
}
}
} else {
log("Check for EAP_SIM_AKA, networks is null!");
}
}
///@}
sendSupplicantConnectionChangedBroadcast(true);
transitionTo(mDriverStartedState);
break;
這裡傳送了一個 WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION 廣播,通知上層可以開始掃描了。
然後切換到 DriverStartedState 狀態。切換到該狀態 需要執行 SupplicantStartedState 和 DriverStartedState 的enter函式。
class SupplicantStartedState extends State {
@Override
public void enter() {
if (DBG) loge(getName() + "\n");
/* 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);
///M: for operator plugin @{
if (hasCustomizedAutoConnect()) {
mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
mScreenOn ? defaultInterval : mContext.getResources().getInteger(
R.integer.config_wifi_framework_scan_interval));
} else {
///@}
mSupplicantScanIntervalMs = mFacade.getLongSetting(mContext,
Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
defaultInterval);
///M: for operator plugin @{
}
///@}
//Task3202733 BEGIN-leida-Oct.29,2016
String customMTUSizeStr = mContext.getResources().getString(com.android.internal.R.string.def_framework_wifi_mtu);
Log.d(TAG, "custom MTU size is used, size:" + customMTUSizeStr);
int customMTUSize = Integer.parseInt(customMTUSizeStr);
Log.d(TAG, "custom MTU size is used, size:" + customMTUSize);
if (customMTUSize > 0) {
if (customMTUSize < 1000 || customMTUSize > 1500) {
Log.d(TAG, "Invalid MTU size of wlan0:" + customMTUSize);
} else {
Log.d(TAG, "set MTU size as customizationed:" + customMTUSize);
try {
mNwService.setMtu("wlan0", customMTUSize);
} catch (Exception e) {
Log.d(TAG, "exception in set customization MTU size:" + e);
}
}
} else {
Log.d(TAG, "set MTU size as default:1500");
try {
mNwService.setMtu("wlan0", 1500);
} catch (Exception e) {
Log.d(TAG, "exception in set default MTU size:" + e);
}
}
//Task3202733 END-leida-Oct.29,2016
mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);//設定掃描時間間隔
mWifiNative.setExternalSim(true);
/* turn on use of DFS channels */
mWifiNative.setDfsFlag(true);
setRandomMacOui();
mWifiNative.enableAutoConnect(false);
mCountryCode.setReadyForChange(true);
///M: for operator plugin @{
// if have special network selection and wifi is connected, don't try to associate.
if (mWifiFwkExt != null && mWifiFwkExt.hasNetworkSelection() != 0) {
//don't let supplicant auto connect when wifi initial
mWifiNative.disconnect();
}
///@}
}
這裡主要是設定一下掃描時間間隔,接下來看看 DriverStartedState 的enter函式。
class DriverStartedState extends State {
@Override
public void enter() {
if (DBG) {
logd("DriverStartedState enter");
}
// We can't do this in the constructor because WifiStateMachine is created before the
// wifi scanning service is initialized
if (mWifiScanner == null) {
mWifiScanner = mFacade.makeWifiScanner(mContext, getHandler().getLooper());
mWifiConnectivityManager = new WifiConnectivityManager(mContext,
WifiStateMachine.this, mWifiScanner, mWifiConfigManager, mWifiInfo,
mWifiQualifiedNetworkSelector, mWifiInjector,
getHandler().getLooper());
}
mWifiLogger.startLogging(DBG);
mIsRunning = true;
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);
// Disable legacy multicast filtering, which on some chipsets defaults to enabled.
// Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6
// provisioning. Legacy IPv4 multicast filtering may be re-enabled later via
// IpManager.Callback.setFallbackMulticastFilter()
mWifiNative.stopFilteringMulticastV4Packets();
mWifiNative.stopFilteringMulticastV6Packets();
if (mOperationalMode != CONNECT_MODE) {//此時mOperationalMode == CONNECT_MODE
mWifiNative.disconnect();
mWifiConfigManager.disableAllNetworksNative();
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();
///M: ALPS02028415 reset scan count
mWifiOnScanCount = 0;
// Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
transitionTo(mDisconnectedState);
///M: N eac may be google mistake
//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());
// Inform WifiConnectivtyManager the screen state in case
// WifiConnectivityManager missed the last screen update because
// it was not started yet.
mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
}
mWifiNative.setPowerSave(true);
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.
}
}
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);
// Enable link layer stats gathering
mWifiNative.setWifiLinkLayerStats("wlan0", 1);
}
這裡由於藍芽和wifi模組有衝突,將通過 setBluetoothCoexistenceScanMode 函式,是wlan晶片做出調整。
最後切換到 DisconnectedState 狀態。connectModeState 和 DisconnectedState 的enter函式將被執行。
class ConnectModeState extends State {
@Override
public void enter() {
if (DBG) loge(getName() + "\n");
// Inform WifiConnectivityManager that Wifi is enabled
if (mWifiConnectivityManager != null) {
mWifiConnectivityManager.setWifiEnabled(true);
}
// Inform metrics that Wifi is Enabled (but not yet connected)
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
}
class DisconnectedState extends State {
@Override
public void enter() {
if (DBG) loge(getName() + "\n");
// We dont scan frequently if this is a temporary disconnect
// due to p2p
if (mTemporarilyDisconnectWifi) {
mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
return;
}
if (isTemporarilyDontReconnectWifi()) {
return;
}
if (DBG) {
logd(" Enter DisconnectedState screenOn=" + mScreenOn);
}
/** clear the roaming state, if we were roaming, we failed */
mAutoRoaming = false;
if (mWifiConnectivityManager != null) {
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
}
/**
* 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()
&& mWifiConfigManager.getSavedNetworks().size() == 0) {
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
mDisconnectedTimeStamp = System.currentTimeMillis();
}
接下來 Disconnected 將處理被延遲的兩條訊息。
case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 != CONNECT_MODE) {
mOperationalMode = message.arg1;
mWifiConfigManager.disableAllNetworksNative();
if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
setWifiState(WIFI_STATE_DISABLED);
}
transitionTo(mScanModeState);
}
mWifiConfigManager.
setAndEnableLastSelectedConfiguration(
WifiConfiguration.INVALID_NETWORK_ID);
break;
case CMD_START_DRIVER:
if (mOperationalMode == CONNECT_MODE) {
mWifiConfigManager.enableAllNetworks();
}
break;
其中 CMD_SET_OPERATIONAL_MODE 訊息在 Disconnected 中被處理,
進行設定上一次的網路配置CMD_START_DRIVER 訊息則是在 Disconnected 的 父狀態
DriverStartedState 中被處理,目的是開啟所有的網路。
至此,wifi啟動流程到這裡就結束了,接下來 WifiStateMachine 就停留在這個狀態等待下一個訊息了。