wifi啟動流程簡介一(app 到 framework)
1.wifi啟動流程簡介
使用者可以通過systemUi和設定裡的WiFi開關開啟WiFi,這時候會呼叫到wifi framework的相關介面,繼而再繼續往下啟用具體的硬體完成WiFi啟動流程,我只對應用到framework層有些簡單的瞭解,本篇也主要注重framework這一塊,app層沒啥好說的。
2.WiFi啟動流程梳理
我之前是負責設定模組的,systemUi程式碼雖然看過,但是不是很熟,所以WiFi開啟流程還是從設定的WiFi開關開始梳理吧,不考慮開啟飛航模式後開啟WiFi的情況=-=
2.1 設定啟動WiFi
設定這邊說到底其實就是監控WiFi開關的變化,然後根據開關走對應的邏輯處理。
兩個比較重要的類:
1)WifiSettings:設定中wifi主介面所對應的程式碼
2)WifiEnabler:設定中負責wifi開關開啟和關閉事件處理的類
/aosp/packages/apps/Settings$ vim ./src/com/android/settings/wifi/WifiSettings.java
-
/**
-
* @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
-
*/
-
private WifiEnabler createWifiEnabler() {
-
final SettingsActivity activity = (SettingsActivity) getActivity();
-
return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()),
-
mMetricsFeatureProvider);
-
}
/aosp/packages/apps/Settings$ vim ./src/com/android/settings/widget/SwitchBarController.java
-
public class SwitchBarController extends SwitchWidgetController implements
-
SwitchBar.OnSwitchChangeListener {
-
private final SwitchBar mSwitchBar;
-
public SwitchBarController(SwitchBar switchBar) {
-
mSwitchBar = switchBar;
-
}
-
...
-
@Override
-
public void startListening() {
-
mSwitchBar.addOnSwitchChangeListener(this);
-
}
-
...
-
@Override
-
public void onSwitchChanged(Switch switchView, boolean isChecked) {
-
if (mListener != null) {
-
mListener.onSwitchToggled(isChecked);
-
}
-
}
/aosp/packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
-
@VisibleForTesting
-
WifiEnabler(Context context, SwitchWidgetController switchWidget,
-
MetricsFeatureProvider metricsFeatureProvider,
-
ConnectivityManagerWrapper connectivityManagerWrapper) {
-
mContext = context;
-
mSwitchWidget = switchWidget;
-
mSwitchWidget.setListener(this);
-
mMetricsFeatureProvider = metricsFeatureProvider;
-
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-
mConnectivityManager = connectivityManagerWrapper;
-
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);
-
setupSwitchController();
-
}
mSwitchWidget.setListener(this); mMetricsFeatureProvider = metricsFeatureProvider; mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mConnectivityManager = connectivityManagerWrapper; 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); setupSwitchController(); }
-
public void setupSwitchController() {
-
final int state = mWifiManager.getWifiState();
-
handleWifiStateChanged(state);
-
if (!mListeningToOnSwitchChange) {
-
mSwitchWidget.startListening();
-
mListeningToOnSwitchChange = true;
-
}
-
mSwitchWidget.setupView();
-
}
-
@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/listenenr loop.
-
mSwitchWidget.setChecked(false);
-
return false;
-
}
-
// Disable tethering if enabling Wifi
-
if (mayDisableTethering(isChecked)) {
-
mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
-
}
-
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;
-
}
mWifiManager.setWifiEnabled(isChecked)) { // Error mSwitchWidget.setEnabled(true); Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show(); } return true; }
看到這裡其實發現應用層開啟和關閉WiFi就是呼叫了下WifiManager的setWifiEabled(boolean)介面即可。
2.2 WiFi framework
看下WifiManager的setWifiEabled(boolean)介面
framework/base/wifi/java/android/net/wifi/WifiManager.java
-
/**
-
* Enable or disable Wi-Fi.
-
*
-
* Note: This method will return false if wifi cannot be enabled (e.g., an incompatible mode
-
* where the user has enabled tethering or Airplane Mode).
-
*
-
* Applications need to have the {@link android.Manifest.permission#CHANGE_WIFI_STATE}
-
* permission to toggle wifi. Callers without the permissions will trigger a
-
* {@link java.lang.SecurityException}.
-
*
-
* @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). False if wifi cannot be toggled on/off when the
-
* request is made.
-
*/
-
public boolean setWifiEnabled(boolean enabled) {
-
try {
-
return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
-
} catch (RemoteException e) {
-
throw e.rethrowFromSystemServer();
-
}
-
}
2.2.1 WifiService是什麼
而mService是啥呢
-
IWifiManager mService;
-
/**
-
* Create a new WifiManager instance.
-
* Applications will almost always want to use
-
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
-
* the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
-
* @param context the application context
-
* @param service the Binder interface
-
* @hide - hide this because it takes in a parameter of type IWifiManager, which
-
* is a system private class.
-
*/
-
public WifiManager(Context context, IWifiManager service, Looper looper) {
-
mContext = context;
-
mService = service;
-
mLooper = looper;
-
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
-
}
應用層的WifiManager都是這麼來的
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
那真正的WifiManager例項是誰來new出來的呢?
有這麼一個類:
/framework/base/core/java/android/app/SystemServiceRegistry.java
-
registerService(Context.WIFI_SERVICE, WifiManager.class,
-
new CachedServiceFetcher<WifiManager>() {
-
@Override
-
public WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-
IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
-
IWifiManager service = IWifiManager.Stub.asInterface(b);
-
return new WifiManager(ctx.getOuterContext(), service,
-
ConnectivityThread.getInstanceLooper());
-
}});
它有個靜態程式碼塊,大致如下,負責建立各種manager例項
-
final class SystemServiceRegistry {
-
...
-
// Not instantiable.
-
private SystemServiceRegistry() { }
-
static {
-
registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
-
new CachedServiceFetcher<AccessibilityManager>() {
-
@Override
-
public AccessibilityManager createService(ContextImpl ctx) {
-
return AccessibilityManager.getInstance(ctx);
-
}});
-
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
-
new CachedServiceFetcher<CaptioningManager>() {
-
@Override
-
public CaptioningManager createService(ContextImpl ctx) {
-
return new CaptioningManager(ctx);
-
}});
-
registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
-
new CachedServiceFetcher<AccountManager>() {
-
@Override
-
public AccountManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-
IBinder b = ServiceManager.getServiceOrThrow(Context.ACCOUNT_SERVICE);
-
IAccountManager service = IAccountManager.Stub.asInterface(b);
-
return new AccountManager(ctx, service);
-
}});
-
...
-
}
而呼叫呢,則看我們之前說的應用層ContextImpl的getSystemService方法
-
// The system service cache for the system services that are cached per-ContextImpl.
-
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
-
@Override
-
public Object getSystemService(String name) {
-
return SystemServiceRegistry.getSystemService(this, name);
-
}
SystemServiceRegistry
-
// Service registry information.
-
// This information is never changed once static initialization has completed.
-
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
-
new HashMap<Class<?>, String>();
-
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
-
new HashMap<String, ServiceFetcher<?>>();
-
/**
-
* Gets a system service from a given context.
-
*/
-
public static Object getSystemService(ContextImpl ctx, String name) {
-
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
-
return fetcher != null ? fetcher.getService(ctx) : null;
-
}
-
/**
-
* Statically registers a system service with the context.
-
* This method must be called during static initialization only.
-
*/
-
private static <T> void registerService(String serviceName, Class<T> serviceClass,
-
ServiceFetcher<T> serviceFetcher) {
-
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
-
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
-
}
這裡主要就弄清了一件事,WifiManager是aidl的客戶端,具體邏輯還是要去看服務端的。即看下Service對應程式碼:
IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
-
// Wifi Service must be started first for wifi-related services.
-
traceBeginAndSlog("StartWifi");
-
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-
traceEnd();
-
private static final String WIFI_SERVICE_CLASS =
-
"com.android.server.wifi.WifiService";
這裡感覺是有關的。
ServiceManager是這樣的:
-
public final class ServiceManager {
-
private static final String TAG = "ServiceManager";
-
/**
-
* Returns a reference to a service with the given name.
-
*
-
* @param name the name of the service to get
-
* @return a reference to the service, or <code>null</code> if the service doesn't exist
-
*/
-
public static IBinder getService(String name) {
-
try {
-
IBinder service = sCache.get(name);
-
if (service != null) {
-
return service;
-
} else {
-
return Binder.allowBlocking(getIServiceManager().getService(name));
-
}
-
} catch (RemoteException e) {
-
Log.e(TAG, "error in getService", e);
-
}
-
return null;
-
}
-
/**
-
* Returns a reference to a service with the given name, or throws
-
* {@link NullPointerException} if none is found.
-
*
-
* @hide
-
*/
-
public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
-
final IBinder binder = getService(name);
-
if (binder != null) {
-
return binder;
-
} else {
-
throw new ServiceNotFoundException(name);
-
}
-
}
-
...
-
/**
-
* This is only intended to be called when the process is first being brought
-
* up and bound by the activity manager. There is only one thread in the process
-
* at that time, so no locking is done.
-
*
-
* @param cache the cache of service references
-
* @hide
-
*/
-
public static void initServiceCache(Map<String, IBinder> cache) {
-
if (sCache.size() != 0) {
-
throw new IllegalStateException("setServiceCache may only be called once");
-
}
-
sCache.putAll(cache);
-
}
可以看到Service的IBinder要麼從cache裡取出來的,要麼getIServiceManager().getService(name)取到的,那就先看下initServiceCache是在哪裡呼叫的?
./base/core/java/android/app/ActivityThread.java
-
public final void bindApplication(String processName, ApplicationInfo appInfo,
-
List<ProviderInfo> providers, ComponentName instrumentationName,
-
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
-
IInstrumentationWatcher instrumentationWatcher,
-
IUiAutomationConnection instrumentationUiConnection, int debugMode,
-
boolean enableBinderTracking, boolean trackAllocation,
-
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
-
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
-
String buildSerial) {
-
if (services != null) {
-
// Setup the service cache in the ServiceManager
-
ServiceManager.initServiceCache(services);
-
}
那bindApplication是在 哪裡呼叫的呢?
AMS:
-
if (app.instr != null) {
-
thread.bindApplication(processName, appInfo, providers,
-
app.instr.mClass,
-
profilerInfo, app.instr.mArguments,
-
app.instr.mWatcher,
-
app.instr.mUiAutomationConnection, testMode,
-
mBinderTransactionTrackingEnabled, enableTrackAllocation,
-
isRestrictedBackupMode || !normalMode, app.persistent,
-
new Configuration(getGlobalConfiguration()), app.compat,
-
getCommonServicesLocked(app.isolated),
-
mCoreSettingsObserver.getCoreSettingsLocked(),
-
buildSerial);
-
} else {
-
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
-
null, null, null, testMode,
-
mBinderTransactionTrackingEnabled, enableTrackAllocation,
-
isRestrictedBackupMode || !normalMode, app.persistent,
-
new Configuration(getGlobalConfiguration()), app.compat,
-
getCommonServicesLocked(app.isolated),
-
mCoreSettingsObserver.getCoreSettingsLocked(),
-
buildSerial);
-
}
-
/**
-
* Initialize the application bind args. These are passed to each
-
* process when the bindApplication() IPC is sent to the process. They're
-
* lazily setup to make sure the services are running when they're asked for.
-
*/
-
private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) {
-
// Isolated processes won't get this optimization, so that we don't
-
// violate the rules about which services they have access to.
-
if (isolated) {
-
if (mIsolatedAppBindArgs == null) {
-
mIsolatedAppBindArgs = new HashMap<>();
-
mIsolatedAppBindArgs.put("package", ServiceManager.getService("package"));
-
}
-
return mIsolatedAppBindArgs;
-
}
-
if (mAppBindArgs == null) {
-
mAppBindArgs = new HashMap<>();
-
// Setup the application init args
-
mAppBindArgs.put("package", ServiceManager.getService("package"));
-
mAppBindArgs.put("window", ServiceManager.getService("window"));
-
mAppBindArgs.put(Context.ALARM_SERVICE,
-
ServiceManager.getService(Context.ALARM_SERVICE));
-
}
-
return mAppBindArgs;
-
}
這裡少了wifi相關的Service,所以還是走的
getIServiceManager().getService(name)
-
private static IServiceManager getIServiceManager() {
-
if (sServiceManager != null) {
-
return sServiceManager;
-
}
-
// Find the service manager
-
sServiceManager = ServiceManagerNative
-
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
-
return sServiceManager;
-
}
“從最表層的API過度到JNI層,然後與Lib層通訊”什麼鬼,我只是個java工程師=-=
先給個結論,wifiManager呼叫介面是呼叫到WifiServiceImpl那邊去了。
-------------------------------2018/6/19日更新,native層的正推沒辦法了,從倒推開始吧----------------------------------------
之前講過WifiService是在SystemServer啟動起來的,流程如下:
SystemServer.java:
-
// Wifi Service must be started first for wifi-related services.
-
traceBeginAndSlog("StartWifi");
-
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-
traceEnd();
SystemServiceManager:
簡單來說就是呼叫下WIFI_SERVICE_CLASS("com.android.server.wifi.WifiService")的構造器和onStart方法
-
/**
-
* Starts a service by class name.
-
*
-
* @return The service instance.
-
*/
-
@SuppressWarnings("unchecked")
-
public SystemService startService(String className) {
-
final Class<SystemService> serviceClass;
-
try {
-
serviceClass = (Class<SystemService>)Class.forName(className);
-
} catch (ClassNotFoundException ex) {
-
Slog.i(TAG, "Starting " + className);
-
throw new RuntimeException("Failed to create service " + className
-
+ ": service class not found, usually indicates that the caller should "
-
+ "have called PackageManager.hasSystemFeature() to check whether the "
-
+ "feature is available on this device before trying to start the "
-
+ "services that implement it", ex);
-
}
-
return startService(serviceClass);
-
}
-
/**
-
* Creates and starts a system service. The class must be a subclass of
-
* {@link com.android.server.SystemService}.
-
*
-
* @param serviceClass A Java class that implements the SystemService interface.
-
* @return The service instance, never null.
-
* @throws RuntimeException if the service fails to start.
-
*/
-
@SuppressWarnings("unchecked")
-
public <T extends SystemService> T startService(Class<T> serviceClass) {
-
try {
-
final String name = serviceClass.getName();
-
Slog.i(TAG, "Starting " + name);
-
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);
-
// Create the service.
-
if (!SystemService.class.isAssignableFrom(serviceClass)) {
-
throw new RuntimeException("Failed to create " + name
-
+ ": service must extend " + SystemService.class.getName());
-
}
-
final T service;
-
try {
-
Constructor<T> constructor = serviceClass.getConstructor(Context.class);
-
service = constructor.newInstance(mContext);
-
} catch (InstantiationException ex) {
-
throw new RuntimeException("Failed to create service " + name
-
+ ": service could not be instantiated", ex);
-
} catch (IllegalAccessException ex) {
-
throw new RuntimeException("Failed to create service " + name
-
+ ": service must have a public constructor with a Context argument", ex);
-
} catch (NoSuchMethodException ex) {
-
throw new RuntimeException("Failed to create service " + name
-
+ ": service must have a public constructor with a Context argument", ex);
-
} catch (InvocationTargetException ex) {
-
throw new RuntimeException("Failed to create service " + name
-
+ ": service constructor threw an exception", ex);
-
}
-
startService(service);
-
return service;
-
} finally {
-
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
}
-
}
-
public void startService(@NonNull final SystemService service) {
-
// Register it.
-
mServices.add(service);
-
// Start it.
-
long time = SystemClock.elapsedRealtime();
-
try {
-
service.onStart();
-
} catch (RuntimeException ex) {
-
throw new RuntimeException("Failed to start service " + service.getClass().getName()
-
+ ": onStart threw an exception", ex);
-
}
-
warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
-
}
WifiService:
-
public final class WifiService extends SystemService {
-
private static final String TAG = "WifiService";
-
final WifiServiceImpl mImpl;
-
public WifiService(Context context) {
-
super(context);
-
mImpl = new WifiServiceImpl(context, new WifiInjector(context), new WifiAsyncChannel(TAG));
-
}
-
@Override
-
public void onStart() {
-
Log.i(TAG, "Registering " + Context.WIFI_SERVICE);
-
publishBinderService(Context.WIFI_SERVICE, mImpl);
-
}
SystemService:
-
protected final void publishBinderService(String name, IBinder service) {
-
publishBinderService(name, service, false);
-
}
-
/**
-
* Publish the service so it is accessible to other services and apps.
-
*/
-
protected final void publishBinderService(String name, IBinder service,
-
boolean allowIsolated) {
-
ServiceManager.addService(name, service, allowIsolated);
-
}
這邊的addService方法和getService方法是對應起來的,一個get,一個add。
所以之前的getService方法就是WifiServiceImpl。
IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
2.2.2 Service的WiFi啟動流程
先看下WifiServiceImpl流程:
-
public class WifiServiceImpl extends IWifiManager.Stub {
-
/**
-
* 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 {
-
enforceChangePermission();
-
Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
-
+ ", uid=" + Binder.getCallingUid() + ", package=" + packageName);
-
mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName)
-
.c(Binder.getCallingUid()).c(enable).flush();
-
boolean isFromSettings = checkNetworkSettingsPermission(
-
Binder.getCallingPid(), Binder.getCallingUid());
-
// If Airplane mode is enabled, only Settings is allowed to toggle Wifi
-
if (mSettingsStore.isAirplaneModeOn() && !isFromSettings) {
-
mLog.info("setWifiEnabled in Airplane mode: only Settings can enable wifi").flush();
-
return false;
-
}
-
// If SoftAp is enabled, only Settings is allowed to toggle wifi
-
boolean apEnabled =
-
mWifiStateMachine.syncGetWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED;
-
if (apEnabled && !isFromSettings) {
-
mLog.info("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
-
return false;
-
}
-
/*
-
* 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);
-
}
-
if (mPermissionReviewRequired) {
-
final int wiFiEnabledState = getWifiEnabledState();
-
if (enable) {
-
if (wiFiEnabledState == WifiManager.WIFI_STATE_DISABLING
-
|| wiFiEnabledState == WifiManager.WIFI_STATE_DISABLED) {
-
if (startConsentUi(packageName, Binder.getCallingUid(),
-
WifiManager.ACTION_REQUEST_ENABLE)) {
-
return true;
-
}
-
}
-
} else if (wiFiEnabledState == WifiManager.WIFI_STATE_ENABLING
-
|| wiFiEnabledState == WifiManager.WIFI_STATE_ENABLED) {
-
if (startConsentUi(packageName, Binder.getCallingUid(),
-
WifiManager.ACTION_REQUEST_DISABLE)) {
-
return true;
-
}
-
}
-
}
-
mWifiController.sendMessage(CMD_WIFI_TOGGLED);
-
return true;
-
}
mWifiController.sendMessage(CMD_WIFI_TOGGLED); return true; }
mSettingsStore.handleWifiToggled(enable)設定一下SettingsProvider中儲存的WIFI_ON的值
-
private void persistWifiState(int state) {
-
final ContentResolver cr = mContext.getContentResolver();
-
mPersistWifiState = state;
-
Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state);
-
}
-
/* Values tracked in Settings.Global.WIFI_ON */
-
static final int WIFI_DISABLED = 0;
-
static final int WIFI_ENABLED = 1;
-
/* Wifi enabled while in airplane mode */
-
private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2;
-
/* Wifi disabled due to airplane mode on */
-
private static final int WIFI_DISABLED_AIRPLANE_ON = 3;
-
public synchronized boolean handleWifiToggled(boolean wifiEnabled) {
-
// Can Wi-Fi be toggled in airplane mode ?
-
if (mAirplaneModeOn && !isAirplaneToggleable()) {
-
return false;
-
}
-
if (wifiEnabled) {
-
if (mAirplaneModeOn) {
-
persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
-
} else {
-
persistWifiState(WIFI_ENABLED);
-
}
-
} else {
-
// When wifi state is disabled, we do not care
-
// if airplane mode is on or not. The scenario of
-
// wifi being disabled due to airplane mode being turned on
-
// is handled handleAirplaneModeToggled()
-
persistWifiState(WIFI_DISABLED);
-
}
-
return true;
-
}
這裡接著會呼叫到WifiController,WifiController是個狀態機設計模式,也就是說會根據WiFi的不同狀態決定處理WiFi訊息的不同邏輯。
初始化邏輯如下:
-
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);
2.2.3 WifiController
WifiController的狀態比較多,而我比較關注從關閉到開啟的狀態變化,即:
-
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;
-
mWifiStateMachine.clearANQPCache();
-
}
-
@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) {
-
// wifi is toggled, we need to explicitly tell WifiStateMachine that we
-
// are headed to connect mode before going to the DeviceActiveState
-
// since that will start supplicant and WifiStateMachine may not know
-
// what state to head to (it might go to scan mode).
-
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
-
transitionTo(mDeviceActiveState);
-
} else {
-
checkLocksAndTransitionWhenDeviceIdle();
-
}
-
} else if (mSettingsStore.isScanAlwaysAvailable()) {
-
transitionTo(mStaDisabledWithScanState);
-
}
-
break;
mSettingsStore.isWifiToggleEnabled()) { if (doDeferEnable(msg)) { if (mHaveDeferredEnable) { // have 2 toggles now, inc serial number an ignore both mDeferredEnableSerialNumber++; } mHaveDeferredEnable = !mHaveDeferredEnable; break; } if (mDeviceIdle == false) { // wifi is toggled, we need to explicitly tell WifiStateMachine that we // are headed to connect mode before going to the DeviceActiveState // since that will start supplicant and WifiStateMachine may not know // what state to head to (it might go to scan mode). mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE); transitionTo(mDeviceActiveState); } else { checkLocksAndTransitionWhenDeviceIdle(); } } else if (mSettingsStore.isScanAlwaysAvailable()) { transitionTo(mStaDisabledWithScanState); } break;
現在就上面一串程式碼挨個解析。
WifiServiceImpl在走到WifiController之前有提及修改了一下SettingsProvider,其實也順帶改了一下WifiSettingsStore的mPersistWifiState值,用來標記wifi狀態。
persistWifiState(WIFI_ENABLED);
-
public synchronized boolean isWifiToggleEnabled() {
-
if (!mCheckSavedStateAtBoot) {
-
mCheckSavedStateAtBoot = true;
-
if (testAndClearWifiSavedState()) return true;
-
}
-
if (mAirplaneModeOn) {
-
return mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE;
-
} else {
-
return mPersistWifiState != WIFI_DISABLED;
-
}
-
}
收到訊息也不是立刻處理的:
-
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;
-
}
-
...
-
mReEnableDelayMillis = mFacade.getLongSetting(mContext,
-
Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS);
-
/**
-
* See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}. This is the default value if a
-
* Settings.Global value is not present. This is the minimum time after wifi is disabled
-
* we'll act on an enable. Enable requests received before this delay will be deferred.
-
*/
-
private static final long DEFAULT_REENABLE_DELAY_MS = 500;
wifi關閉後立刻開啟有可能流程還沒走完導致問題(壓力測試會頻繁開關WiFi),所以這邊Google應該考慮到這點加了個最短時限500ms,如果短於這時間,就強迫補個差來個500+5ms的延時。
以小於500ms的間隔傳送訊息會由於mDeferredEnableSerialNumber值自增導致前一個訊息失效。
-
case CMD_DEFERRED_TOGGLE:
-
if (msg.arg1 != mDeferredEnableSerialNumber) {
-
log("DEFERRED_TOGGLE ignored due to serial mismatch");
-
break;
-
}
-
log("DEFERRED_TOGGLE handled");
-
sendMessage((Message)(msg.obj));
-
break;
接著狀態機會走到如下邏輯中去,除了WifiStateMachine外,還會將狀態改為mDeviceActiveState。
-
// wifi is toggled, we need to explicitly tell WifiStateMachine that we
-
// are headed to connect mode before going to the DeviceActiveState
-
// since that will start supplicant and WifiStateMachine may not know
-
// what state to head to (it might go to scan mode).
-
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
-
transitionTo(mDeviceActiveState);
由於是一個類,看下StaEnabledState(DeviceActiveState的父狀態)和DeviceActiveState,狀態機設計模式狀態改變會先走進父狀態的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.setHighPerfModeEnabled(false);
-
}
可以看到其實是呼叫順序
- mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setSupplicantRunning(true);
- mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setHighPerfModeEnabled(false);
2.2.4 WifiStateMachine
wifiStateMachine也是一個狀態機:
-
// CHECKSTYLE:OFF IndentationCheck
-
addState(mDefaultState);
-
addState(mInitialState, mDefaultState);
-
addState(mSupplicantStartingState, mDefaultState);
-
addState(mSupplicantStartedState, mDefaultState);
-
addState(mScanModeState, mSupplicantStartedState);
-
addState(mConnectModeState, mSupplicantStartedState);
-
addState(mL2ConnectedState, mConnectModeState);
-
addState(mObtainingIpState, mL2ConnectedState);
-
addState(mConnectedState, mL2ConnectedState);
-
addState(mRoamingState, mL2ConnectedState);
-
addState(mDisconnectingState, mConnectModeState);
-
addState(mDisconnectedState, mConnectModeState);
-
addState(mWpsRunningState, mConnectModeState);
-
addState(mWaitForP2pDisableState, mSupplicantStartedState);
-
addState(mSupplicantStoppingState, mDefaultState);
-
addState(mSoftApState, mDefaultState);
-
// CHECKSTYLE:ON IndentationCheck
這裡看下WifiController下發的4個操作
1、3)setOperationalMode:
-
/**
-
* Track the state of Wifi connectivity. All event handling is done here,
-
* and all changes in connectivity state are initiated here.
-
*
-
* Wi-Fi now supports three modes of operation: Client, SoftAp and p2p
-
* In the current implementation, we support concurrent wifi p2p and wifi operation.
-
* The WifiStateMachine handles SoftAp and Client operations while WifiP2pService
-
* handles p2p operation.
-
*
-
* @hide
-
*/
-
public class WifiStateMachine extends StateMachine implements WifiNative.WifiRssiEventHandler,
-
WifiMulticastLockManager.FilterController {
-
/**
-
* TODO: doc
-
*/
-
public void setOperationalMode(int mode) {
-
if (mVerboseLoggingEnabled) log("setting operational mode to " + String.valueOf(mode));
-
sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
-
})
2)setSupplicantRunning
/**
-
* TODO: doc
-
*/
-
public void setSupplicantRunning(boolean enable) {
-
if (enable) {
-
sendMessage(CMD_START_SUPPLICANT);
-
} else {
-
sendMessage(CMD_STOP_SUPPLICANT);
-
}
-
}
4) setHighPerfModeEnabled
-
/**
-
* Set high performance mode of operation.
-
* Enabling would set active power mode and disable suspend optimizations;
-
* disabling would set auto power mode and enable suspend optimizations
-
*
-
* @param enable true if enable, false otherwise
-
*/
-
public void setHighPerfModeEnabled(boolean enable) {
-
sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
-
}
來看下初始狀態對訊息的處理
-
class InitialState extends State {
-
private void cleanup() {
-
// Tearing down the client interfaces below is going to stop our supplicant.
-
mWifiMonitor.stopAllMonitoring();
-
mDeathRecipient.unlinkToDeath();
-
mWifiNative.tearDown();
-
}
-
@Override
-
public void enter() {
-
mWifiStateTracker.updateState(WifiStateTracker.INVALID);
-
cleanup();
-
}
-
@Override
-
public boolean processMessage(Message message) {
-
logStateAndMessage(message, this);
-
switch (message.what) {
-
case CMD_START_SUPPLICANT:
-
Pair<Integer, IClientInterface> statusAndInterface =
-
mWifiNative.setupForClientMode();
-
if (statusAndInterface.first == WifiNative.SETUP_SUCCESS) {
-
mClientInterface = statusAndInterface.second;
-
} else {
-
incrementMetricsForSetupFailure(statusAndInterface.first);
-
}
-
if (mClientInterface == null
-
|| !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) {
-
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
-
cleanup();
-
break;
-
}
-
try {
-
// A runtime crash or shutting down AP mode can leave
-
// IP addresses configured, and this affects
-
// connectivity when supplicant starts up.
-
// Ensure we have no IP addresses before a supplicant start.
-
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);
-
}
-
if (!mWifiNative.enableSupplicant()) {
-
loge("Failed to start supplicant!");
-
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
-
cleanup();
-
break;
-
}
-
if (mVerboseLoggingEnabled) log("Supplicant start successful");
-
mWifiMonitor.startMonitoring(mInterfaceName, true);
-
mWifiInjector.getWifiLastResortWatchdog().clearAllFailureCounts();
-
setSupplicantLogLevel();
-
transitionTo(mSupplicantStartingState);
-
break;
-
case CMD_START_AP:
-
transitionTo(mSoftApState);
-
break;
-
case CMD_SET_OPERATIONAL_MODE:
-
mOperationalMode = message.arg1;
-
if (mOperationalMode != DISABLED_MODE) {
-
sendMessage(CMD_START_SUPPLICANT);
-
}
-
break;
-
default:
-
return NOT_HANDLED;
-
}
-
return HANDLED;
-
}
-
}
在InitialState 中 1、 3 步最後走的邏輯和第2步是一樣的,都是發出了一個CMD_START_SUPPLICANT訊息,不是很懂這塊處理邏輯=-=搜了下真正對CONNECT_MODE有處理的是以下狀態,應該是第3步發出的訊息會得到ScanModeState的處理:
-
class ScanModeState extends State {
-
private int mLastOperationMode;
-
@Override
-
public void enter() {
-
mLastOperationMode = mOperationalMode;
-
mWifiStateTracker.updateState(WifiStateTracker.SCAN_MODE);
-
}
-
@Override
-
public boolean processMessage(Message message) {
-
logStateAndMessage(message, this);
-
switch(message.what) {
-
case CMD_SET_OPERATIONAL_MODE:
-
if (message.arg1 == CONNECT_MODE) {
-
mOperationalMode = CONNECT_MODE;
-
setWifiState(WIFI_STATE_ENABLING);
-
transitionTo(mDisconnectedState);
-
} else if (message.arg1 == DISABLED_MODE) {
-
transitionTo(mSupplicantStoppingState);
-
}
-
// Nothing to do
-
break;
-
// Handle scan. All the connection related commands are
-
// handled only in ConnectModeState
-
case CMD_START_SCAN:
-
handleScanRequest(message);
-
break;
-
default:
-
return NOT_HANDLED;
-
}
-
return HANDLED;
-
}
-
}
先看下接收到該訊息進行的關鍵操作:
- mWifiNative.enableSupplicant()
- mWifiMonitor.startMonitoring(mInterfaceName, true);
- 切換到SupplicantStartingState狀態
PS:
-
/**
-
* Enable wpa_supplicant via wificond.
-
* @return Returns true on success.
-
*/
-
public boolean enableSupplicant() {
-
return mWificondControl.enableSupplicant();
-
}
-
/**
-
* Start Monitoring for wpa_supplicant events.
-
*
-
* @param iface Name of iface.
-
* TODO: Add unit tests for these once we remove the legacy code.
-
*/
-
public synchronized void startMonitoring(String iface, boolean isStaIface) {
-
if (ensureConnectedLocked()) {
-
setMonitoring(iface, true);
-
broadcastSupplicantConnectionEvent(iface);
-
} else {
-
boolean originalMonitoring = isMonitoring(iface);
-
setMonitoring(iface, true);
-
broadcastSupplicantDisconnectionEvent(iface);
-
setMonitoring(iface, originalMonitoring);
-
Log.e(TAG, "startMonitoring(" + iface + ") failed!");
-
}
-
<}