本文简单介绍下WiFi打开与关闭流程,参考源码Android P。
一、WiFi 开机自动打开流程
系统服务启动的时候会启动WifiService,在SystemService.PHASE_SYSTEM_SERVICES_READY的时候,会调用 WifiServiceImpl#checkAndStartWifi(),获取Wi-Fi开关,持久化存储的值。
然后判断是否需要打开WiFi。
/**
* Check if we are ready to start wifi.
*
* First check if we will be restarting system services to decrypt the device. If the device is
* not encrypted, check if Wi-Fi needs to be enabled and start if needed
*
* This function is used only at boot time.
*/
public void checkAndStartWifi() {
// First check if we will end up restarting WifiService
if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
Log.d(TAG, "Device still encrypted. Need to restart SystemServer. Do not start wifi.");
return;
}
// Check if wi-fi needs to be enabled
boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled();
........
// If we are already disabled (could be due to airplane mode), avoid changing persist
// state here
if (wifiEnabled) {
try {
setWifiEnabled(mContext.getPackageName(), wifiEnabled);
} catch (RemoteException e) {
/* ignore - local call */
}
}
}
二、系统设置主动开启WiFi
1. WiFi开关切换,在WifiEnabler的SwitchToggled中会调用WifiManager.setWifiEnabled方法。
@Override
public boolean onSwitchToggled(boolean isChecked) {
//Do nothing if called as a result of a state machine event
if (mStateMachineEvent) {
return true;
}
// 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/listener loop.
mSwitchWidget.setChecked(false);
return false;
}
if (isChecked) {
mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_ON);
} else {
// Log if user was connected at the time of switching off.
mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_WIFI_OFF,
mConnected.get());
}
if (!mWifiManager.setWifiEnabled(isChecked)) {
// Error
mSwitchWidget.setEnabled(true);
Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}
return true;
}
2.?WifiManager中通过AIDL接口调用WifiService的方法。
/**
* Enable or disable Wi-Fi.
* <p>
* Applications must have the {@link android.Manifest.permission#CHANGE_WIFI_STATE}
* permission to toggle wifi.
*
* @param enabled {@code true} to enable, {@code false} to disable.
* @return {@code false} if the request cannot be satisfied; {@code true} indicates that wifi is
* either already in the requested state, or in progress toward the requested state.
* @throws {@link java.lang.SecurityException} if the caller is missing required permissions.
*/
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
3.?WiFiManager.aidl中提供了setWifiEnabled()接口。
boolean setWifiEnabled(String packageName, boolean enable);
4.?WifiServiceImpl中实现WifiManager的接口,在setWifiEnabled函数中会先调用WifiSettingsStore将WiFi开关值进行持久化存储。
然后WifiController发消息CMD_WIFI_TOGGLED 去打开WiFi。
/**
* 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.
*/
@Override
public synchronized boolean setWifiEnabled(String packageName, boolean enable)
throws RemoteException {
........
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;
}
5.?WifiController中对CMD_WIFI_TOGGLED消息进行处理,从初始状态StaDisabledState(如果没有开启wifi_scan_always_enabled功能)切换到DeviceActiveState状态。
class StaDisabledState extends State {
private int mDeferredEnableSerialNumber = 0;
private boolean mHaveDeferredEnable = false;
private long mDisabledTimestamp;
@Override
public void enter() {
mWifiStateMachinePrime.disableWifi();
// Supplicant can't restart right away, so note the time we switched off
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
mWifiStateMachine.clearANQPCache();
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_WIFI_TOGGLED:
if (mSettingsStore.isWifiToggleEnabled()) {
if (doDeferEnable(msg)) {
if (mHaveDeferredEnable) {
// have 2 toggles now, inc serial number and ignore both
mDeferredEnableSerialNumber++;
}
mHaveDeferredEnable = !mHaveDeferredEnable;
break;
}
transitionTo(mDeviceActiveState);
} else if (checkScanOnlyModeAvailable()) {
// only go to scan mode if we aren't in airplane mode
if (mSettingsStore.isAirplaneModeOn()) {
transitionTo(mStaDisabledWithScanState);
}
}
break;
........
}
}
/**
* Parent: StaEnabledState
*
* TODO (b/79209870): merge DeviceActiveState and StaEnabledState into a single state
*/
class DeviceActiveState extends State {
@Override
public void enter() {
mWifiStateMachinePrime.enterClientMode();
mWifiStateMachine.setHighPerfModeEnabled(false);
}
@Override
public boolean processMessage(Message msg) {
if (msg.what == CMD_USER_PRESENT) {
// TLS networks can't connect until user unlocks keystore. KeyStore
// unlocks when the user punches PIN after the reboot. So use this
// trigger to get those networks connected.
if (mFirstUserSignOnSeen == false) {
mWifiStateMachine.reloadTlsNetworksAndReconnect();
}
mFirstUserSignOnSeen = true;
return HANDLED;
} else if (msg.what == CMD_RECOVERY_RESTART_WIFI) {
final String bugTitle;
final String bugDetail;
if (msg.arg1 < SelfRecovery.REASON_STRINGS.length && msg.arg1 >= 0) {
bugDetail = SelfRecovery.REASON_STRINGS[msg.arg1];
bugTitle = "Wi-Fi BugReport: " + bugDetail;
} else {
bugDetail = "";
bugTitle = "Wi-Fi BugReport";
}
if (msg.arg1 != SelfRecovery.REASON_LAST_RESORT_WATCHDOG) {
(new Handler(mWifiStateMachineLooper)).post(() -> {
mWifiStateMachine.takeBugReport(bugTitle, bugDetail);
});
}
return NOT_HANDLED;
}
return NOT_HANDLED;
}
}
进入DeviceActiveState,执行enter()函数,执行enterClientMode,给ModeStateMachine状态机发送CMD_START_CLIENT_MODE消息。
/**
* Method to switch wifi into client mode where connections to configured networks will be
* attempted.
*/
public void enterClientMode() {
changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
}
private class ModeStateMachine extends StateMachine {
........
private boolean checkForAndHandleModeChange(Message message) {
switch(message.what) {
case ModeStateMachine.CMD_START_CLIENT_MODE:
Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
mModeStateMachine.transitionTo(mClientModeActiveState);
break;
case ModeStateMachine.CMD_START_SCAN_ONLY_MODE:
Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode");
mModeStateMachine.transitionTo(mScanOnlyModeActiveState);
break;
case ModeStateMachine.CMD_DISABLE_WIFI:
Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled");
mModeStateMachine.transitionTo(mWifiDisabledState);
break;
default:
return NOT_HANDLED;
}
return HANDLED;
}
........
}
6. 进入ClientModeActiveState,执行enter()函数,WifiInjector创建一个 ClientModeManager,并start。
class ClientModeActiveState extends ModeActiveState {
@Override
public void enter() {
Log.d(TAG, "Entering ClientModeActiveState");
mListener = new ClientListener();
mManager = mWifiInjector.makeClientModeManager(mListener);
mManager.start();
mActiveModeManagers.add(mManager);
updateBatteryStatsWifiState(true);
}
}
/**
* Create a ClientModeManager
*
* @param listener listener for ClientModeManager state changes
* @return a new instance of ClientModeManager
*/
public ClientModeManager makeClientModeManager(ClientModeManager.Listener listener) {
return new ClientModeManager(mContext, mWifiStateMachineHandlerThread.getLooper(),
mWifiNative, listener, mWifiMetrics, mScanRequestProxy, mWifiStateMachine);
}
ClientModeManager 中去启动客户端模式,发送消息CMD_START,进去到IdleState继续处理,处理完毕进入StartedState。
/**
* Start client mode.
*/
public void start() {
mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}
private class IdleState extends State {
@Override
public void enter() {
Log.d(TAG, "entering IdleState");
mClientInterfaceName = null;
mIfaceIsUp = false;
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
updateWifiState(WifiManager.WIFI_STATE_ENABLING,
WifiManager.WIFI_STATE_DISABLED);
mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(
false /* not low priority */, mWifiNativeInterfaceCallback);
if (TextUtils.isEmpty(mClientInterfaceName)) {
Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
updateWifiState(WifiManager.WIFI_STATE_UNKNOWN,
WifiManager.WIFI_STATE_ENABLING);
updateWifiState(WifiManager.WIFI_STATE_DISABLED,
WifiManager.WIFI_STATE_UNKNOWN);
break;
}
sendScanAvailableBroadcast(false);
mScanRequestProxy.enableScanningForHiddenNetworks(false);
mScanRequestProxy.clearScanResults();
transitionTo(mStartedState);
break;
default:
Log.d(TAG, "received an invalid message: " + message);
return NOT_HANDLED;
}
return HANDLED;
}
}
7. 接下来看下WifiNative 中setupInterfaceForClientMode,在其中做了HAL部分的启动(包含check驱动是否已经准备ok,HAL部分是否成功启动),之后再进行wpa_supplicant进程的启动,状态检测进程的启动startMonitoring。
/**
* Setup an interface for Client mode operations.
*
* This method configures an interface in STA mode in all the native daemons
* (wificond, wpa_supplicant & vendor HAL).
*
* @param lowPrioritySta The requested STA has a low request priority (lower probability of
* getting created, higher probability of getting destroyed).
* @param interfaceCallback Associated callback for notifying status changes for the iface.
* @return Returns the name of the allocated interface, will be null on failure.
*/
public String setupInterfaceForClientMode(boolean lowPrioritySta,
@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
if (!startSupplicant()) {
Log.e(TAG, "Failed to start supplicant");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return null;
}
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA);
if (iface == null) {
Log.e(TAG, "Failed to allocate new STA iface");
return null;
}
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface, lowPrioritySta);
if (TextUtils.isEmpty(iface.name)) {
Log.e(TAG, "Failed to create STA iface in vendor HAL");
mIfaceMgr.removeIface(iface.id);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) {
Log.e(TAG, "Failed to setup iface in wificond on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
return null;
}
if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return null;
}
iface.networkObserver = new NetworkObserverInternal(iface.id);
if (!registerNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to register network observer on " + iface);
teardownInterface(iface.name);
return null;
}
mWifiMonitor.startMonitoring(iface.name);
// Just to avoid any race conditions with interface state change callbacks,
// update the interface state before we exit.
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
initializeNwParamsForClientInterface(iface.name);
Log.i(TAG, "Successfully setup " + iface);
return iface.name;
}
}
8. 在ClientModeManager中进行状态的更新,广播WiFi状态改变消息。
/**
* Update Wifi state and send the broadcast.
* @param newState new Wifi state
* @param currentState current wifi state
*/
private void updateWifiState(int newState, int currentState) {
if (!mExpectedStop) {
mListener.onStateChanged(newState);
} else {
Log.d(TAG, "expected stop, not triggering callbacks: newState = " + newState);
}
// Once we report the mode has stopped/failed any other stop signals are redundant
// note: this can happen in failure modes where we get multiple callbacks as underlying
// components/interface stops or the underlying interface is destroyed in cleanup
if (newState == WifiManager.WIFI_STATE_UNKNOWN
|| newState == WifiManager.WIFI_STATE_DISABLED) {
mExpectedStop = true;
}
if (newState == WifiManager.WIFI_STATE_UNKNOWN) {
// do not need to broadcast failure to system
return;
}
mWifiStateMachine.setWifiStateForApiCalls(newState);
final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
9. 系统设置在WifiEnabler中会监听WiFi的状态。
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
handleWifiStateChanged(mWifiManager.getWifiState());
} else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
if (!mConnected.get()) {
handleStateChanged(WifiInfo.getDetailedStateOf((SupplicantState)
intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
}
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO);
mConnected.set(info.isConnected());
handleStateChanged(info.getDetailedState());
}
}
};
三、WiFi关闭流程
与打开流程一样,只是在WifiController中收到CMD_WIFI_TOGGLED消息,会从StaEnabledState切换到mStaDisabledState(如果没有启用wifi_scan_always_enabled功能)。
class StaDisabledState extends State {
private int mDeferredEnableSerialNumber = 0;
private boolean mHaveDeferredEnable = false;
private long mDisabledTimestamp;
@Override
public void enter() {
mWifiStateMachinePrime.disableWifi();
// Supplicant can't restart right away, so note the time we switched off
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
mWifiStateMachine.clearANQPCache();
}
会调用WifiStateMachinePrime.disableWifi()完成wifi的关闭操作。
Android 的WiFi打开关闭流程大致如此,不足之处后续会继续完善。
|