复习并记录下android wifi相关知识,因为下载的是androidp 的source code,因此codebase就用androidP的版本,复习整个android wifi的flow,我大概会分为以下几个部分来讲解
1 android wifi enable flow
2 androi wifi scanning flow
3 android wifi user connect flow
4 android wifi auto connect flow
5 android network type swtich flow
.....
暂时想写的有上面这些,后续想到了再补充
今天这篇来写一下android wifi enable flow
android 发展到现在,wifi flow 从android N开始实际上已经开始了有了很多的变化,可以说整个架构都已经和原来的不一样了。上层与wpa_supplicant 之间 IPC也在将unix socket (android domain socket) 替换成hidl 方式。更有scan get_scan_result这些都是直接建立netlink socket 与kernel 通信而不通过wpa_supplicant。
首先先看下Wifiservice的注册,以及开机自动检测wifi的开启或关闭流程如下:
一般获取WifiManager有两种方式,
1 mWifiManager = mWifiManager = new WifiManager(mContext, mWifiService, mLooper.getLooper());
2 WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE )
第一种比较好理解,就是new一个WifiManager的实例,而第二种实际上也是new 一个WifiManager的实例,只不过是
这个new 实例的过程是在framework层做的,而不是在app层做的.以下讲解下这个流程,先上图,再解析source code
SystemServiceRegistry
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());
}});
而registerService,实际上就是把该类与名称对应存入map中
/**
* 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);
}
使用如下接口获取WifiManger实例mContext.getSystemService(Context.WIFI_SERVICE )则是从map表中查找
Context.java
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
==>
ContextWrapper.java
@Override
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
==>
ConttextImpl.java
1738 @Override
1739 public Object getSystemService(String name) {
1740 return SystemServiceRegistry.getSystemService(this, name);
1741 }
==>SystemServiceRegistry
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
则是用service name在之前注册的map里查找类WifiManger
UI上面打开wifi的flow 如下
app层会调用到WifiManager的接口
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
通过binder 继而call到 WifiServiceImpl.java
public synchronized boolean setWifiEnabled(String packageName, boolean enable)
throws RemoteException {
if (enforceChangePermission(packageName) != MODE_ALLOWED) {
return false;
}
//打印出uid和apk名称,便于debug
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 = mWifiApState == WifiManager.WIFI_AP_STATE_ENABLED;
if (apEnabled && !isFromSettings) {
mLog.info("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
return false;
}
//权限检测,详细可参照另一篇文章
//https://blog.youkuaiyun.com/lpboss/article/details/86610726
/*
* 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;
}
}
}
//给WifiController这个StateMachine发送CMD_WIFI_TOGGLED
mWifiController.sendMessage(CMD_WIFI_TOGGLED);
return true;
}
当前WifiController状态机开机后初始状态会在staDisabledState如下
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) {
//收到WifiServiceImpl发送过来的CMD_WIFI_TOGGLED,在这个子状态里面处理后,最后会跳转到 mDeviceActiveState子状态
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);
}
...
}
WifiController状态机的层级结构如下:
可以看到mStaEnabledState是mDeviceActiveState的父状态,因此跳转到mDeviceActiveState时,会先要进mStaEnabledState的enter,然后再进mDeviceActiveState的enter
DeviceActiveState
public void enter() {
mWifiStateMachinePrime.enterClientMode();
mWifiStateMachine.setHighPerfModeEnabled(false);
}
在enter function里,调到WifiStateMachinePrime.enterClientMode api
enterClientMode
/**
* 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 void changeMode(int newMode) {
mModeStateMachine.sendMessage(newMode);
}
即 发送CMD_START_CLIENT_MODE到ModeStateMachine中
ModeStateMachine当前的状态处在WifiDisabledState ,因此看他的processMessage会对这个cmd做什么处理
class WifiDisabledState extends ModeActiveState {
@Override
public void enter() {
Log.d(TAG, "Entering WifiDisabledState");
mDefaultModeManager.sendScanAvailableBroadcast(mContext, false);
mScanRequestProxy.enableScanningForHiddenNetworks(false);
mScanRequestProxy.clearScanResults();
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "received a message in WifiDisabledState: " + message);
if (checkForAndHandleModeChange(message)) {
return HANDLED;
}
return NOT_HANDLED;
}
private boolean checkForAndHandleModeChange(Message message) {
switch(message.what) {
case ModeStateMachine.CMD_START_CLIENT_MODE:
Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
///////////跳转到 clientModeActiveState子状态
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;
}
跳转到mClientModeActiveState 子状态
public void enter() {
Log.d(TAG, "Entering ClientModeActiveState");
mListener = new ClientListener();
//////调用到WifiInjector里去了。
mManager = mWifiInjector.makeClientModeManager(mListener);
mManager.start();
mActiveModeManagers.add(mManager);
updateBatteryStatsWifiState(true);
}
WifiInjector这个类的用途如google说明:
/**
* WiFi dependency injector. To be used for accessing various WiFi class instances and as a
* handle for mock injection.
*
* Some WiFi class instances currently depend on having a Looper from a HandlerThread that has
* been started. To accommodate this, we have a two-phased approach to initialize and retrieve
* an instance of the WifiInjector.
*/
用来管理很多wifi相关的类,继续往下走
/**
* 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);
}
new一个ClientModeManager的实例,在上面也有说到,ModeStatemachine子状态 mClientModeActiveState enter函数中,还调用该实例的start api ,clientModeManager的用途如下:
/**
* Manager WiFi in Client Mode where we connect to configured networks.
*/
/**
* Start client mode.
*/
public void start() {
mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}
ClientModeStateMachine,这个StateMachine的子状态只有两个idleState, startedState.明显,初始化的时候肯定在idelState
看看idleState的processMessage会针对CMD_START这个cmd做什么处理
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;
调用到WifiNative 的接口setupInterfaceForClientMode,马上就要到开启wpa_supplicant的流程了。一到启动wpa_supplicant的流程以及与wpa_supplicant建立起连接之后,wifi的enable flow就要告一段落了。接着往下吧:注意一下
mClientInterfaceName = mWifiNative.setupInterfaceForClientMode(
false /* not low priority */, mWifiNativeInterfaceCallback);
调用的时候有个参数,是注册进wifinative的call back,当interface有up and down时,会有callback 回来告知clientModeStateMachine.
/**
* 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) {
/*看看wifi是否有实现wifi hal层,如果有,走wifi hal层会有不一样的flow,如果没有也没关系可以继续
往下走,这里有一个有关系的bug,就是androidP,走到p2p flow的时候 ,要求必须有hal层才能开p2p,这
里有跟google沟通过,是一个bug,并不需要强制产商一定要实现wifi hal层,才能用p2p,所以应该是下一个版本会修。*/
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
//开启wpa_supplicant
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;
}
/*androidP启动wpa_supplicant的方式跟之前有差别,以前wpa_supplicant的service一启动就会up
wlan0和p2p0的interface,并要求在此之前要准备好wpa_supplicant.conf和p2p_supplicant.conf配
置文件,但在androidP,则不一样,androidP只是先把wpa_supplicant启动起来,也不带conf.然后后面
再将interface add 进来,因此也可以看到以前在启动wpa_supplicant之前会有个config拷贝的动作在
wpa_supplicant以外做完,但现在则是直接做在了wpa_supplicant调用addInterface里面,这也是
porting androidP要注意的点之一,如果还用之前的wpa_supplicant service,那就会报很多问题*/
if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return null;
}
/*注删interface observer,只要interface有up and down,则会调用observer类里面的callback
function .这个跟之前ModeStateMachine注册进wifiNative的callback会相关,后面会讲到*/
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;
}
/*开始监听wpa_supplicant回报的event.WifiMonitor实现跟之前有差别,但是作用是一样的*/
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;
}
}
wpa_supplicant service 在androidP的写法如下:
service wpa_supplicant /vendor/bin/hw/wpa_supplicant -d \
-puse_p2p_group_interface=1 \
-m/data/vendor/wifi/wpa/p2p_supplicant.conf \
-O/data/vendor/wifi/wpa/sockets \
-g@android:wpa_wlan0
# we will start as root and wpa_supplicant will switch to user wifi
# after setting up the capabilities required for WEXT
# user wifi
# group wifi inet keystore
interface android.hardware.wifi.supplicant@1.0::ISupplicant default
interface android.hardware.wifi.supplicant@1.1::ISupplicant default
class late_start
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
startSupplicant再往下走
/** Helper method invoked to start supplicant if there were no STA ifaces */
private boolean startSupplicant() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyStaIface()) {
if (!mWificondControl.enableSupplicant()) {
Log.e(TAG, "Failed to enable supplicant");
return false;
}
if (!waitForSupplicantConnection()) {
Log.e(TAG, "Failed to connect to supplicant");
return false;
}
if (!mSupplicantStaIfaceHal.registerDeathHandler(
new SupplicantDeathHandlerInternal())) {
Log.e(TAG, "Failed to register supplicant death handler");
return false;
}
}
return true;
}
}
会进到 wifiCondController
/**
* Enable wpa_supplicant via wificond.
* @return Returns true on success.
*/
public boolean enableSupplicant() {
if (!retrieveWificondAndRegisterForDeath()) {
return false;
}
try {
return mWificond.enableSupplicant();
} catch (RemoteException e) {
Log.e(TAG, "Failed to enable supplicant due to remote exception");
}
return false;
}
mWificond,实际上是wificond 这个service的一个client端,通过aidl方式实现的binder IPC,call到 wificond
server.cpp | ~/workspace/android_source/system/connectivity/wificond
Server::enableSupplicant(bool* success) {
*success = supplicant_manager_->StartSupplicant();
return Status::ok();
}
接下来wificond会调用libwifisystem这个库里面的supplicant_manager的api StartSupplicant
bool SupplicantManager::StartSupplicant() ==》这个function主要作用则是用ctl.start 的方式,将上述的wpa_supplicant rc注册的service enable起来。至此wpa_supplicant终于启动了。
接着看WifiMonitor的startMonring
/**
* Start Monitoring for wpa_supplicant events.
*
* @param iface Name of iface.
*/
public synchronized void startMonitoring(String iface) {
if (mVerboseLoggingEnabled) Log.d(TAG, "startMonitoring(" + iface + ")");
setMonitoring(iface, true);
broadcastSupplicantConnectionEvent(iface);
}
/**
* Broadcast the connection to wpa_supplicant event to all the handlers registered for
* this event.
*
* @param iface Name of iface on which this occurred.
*/
public void broadcastSupplicantConnectionEvent(String iface) {
sendMessage(iface, SUP_CONNECTION_EVENT);
}
以前的版本会用这个event在做为判断framework 与wpa_supplicant已经建立连线。让framework走一步动作用,现在androidP看起来好像sta没有在用这个event的感觉。还是我看得有问题,先pass,往下走看看。
上面其实有讲到在setupInterfaceForClientMode,有注册了一个interface 的监听器l,在wpa_supplicant开启后,以及addInterface后,实际上wpa_supplicant就会把interface up起来。那么这时候NetworkObserverInternal的callback
interfaceLinkStateChanged会被调用到。
public void interfaceLinkStateChanged(String ifaceName, boolean unusedIsLinkUp) {
synchronized (mLock) {
final Iface ifaceWithId = mIfaceMgr.getIface(mInterfaceId);
if (ifaceWithId == null) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Received iface link up/down notification on an invalid iface="
+ mInterfaceId);
}
return;
}
final Iface ifaceWithName = mIfaceMgr.getIface(ifaceName);
if (ifaceWithName == null || ifaceWithName != ifaceWithId) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Received iface link up/down notification on an invalid iface="
+ ifaceName);
}
return;
}
onInterfaceStateChanged(ifaceWithName, isInterfaceUp(ifaceName));
}
}
/** Helper method invoked to handle interface change. */
private void onInterfaceStateChanged(Iface iface, boolean isUp) {
synchronized (mLock) {
// Mask multiple notifications with the same state.
if (isUp == iface.isUp) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Interface status unchanged on " + iface + " from " + isUp
+ ", Ignoring...");
}
return;
}
Log.i(TAG, "Interface state changed on " + iface + ", isUp=" + isUp);
if (isUp) {
/*这个看着眼熟吧,这个externalListener就是之前clientModeManager注册进来的callback,
这里interface up 起来之后,会call到之前callback 类的onUp api*/
iface.externalListener.onUp(iface.name);
} else {
iface.externalListener.onDown(iface.name);
if (iface.type == Iface.IFACE_TYPE_STA) {
mWifiMetrics.incrementNumClientInterfaceDown();
} else if (iface.type == Iface.IFACE_TYPE_AP) {
mWifiMetrics.incrementNumSoftApInterfaceDown();
}
}
iface.isUp = isUp;
}
}
紧接着看看之前ClientModeManager onUp之前没做完的事情
@Override
public void onUp(String ifaceName) {
if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) {
sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
}
}
clientModeStateMachine里 在调用完setupInterfaceForClientMode这个之后,已经将State跳转到了StartedState了,因此,看下StartedState会对CMD_INTERFACE_STATUS_CHANGED做什么事情:
case CMD_INTERFACE_STATUS_CHANGED:
boolean isUp = message.arg1 == 1;
onUpChanged(isUp);
break;
private class StartedState extends State {
private void onUpChanged(boolean isUp) {
if (isUp == mIfaceIsUp) {
return; // no change
}
mIfaceIsUp = isUp;
if (isUp) {
Log.d(TAG, "Wifi is ready to use for client mode");
/*发送可以scan的广播,让scan service可以开始做scan动作*/
sendScanAvailableBroadcast(true);
/*与WifiStateMachine交互下面具体看看WifiStateMachine的跳转*/
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE,
mClientInterfaceName);
updateWifiState(WifiManager.WIFI_STATE_ENABLED,
WifiManager.WIFI_STATE_ENABLING);
} else {
if (mWifiStateMachine.isConnectedMacRandomizationEnabled()) {
// Handle the error case where our underlying interface went down if we
// do not have mac randomization enabled (b/72459123).
return;
}
// if the interface goes down we should exit and go back to idle state.
Log.d(TAG, "interface down!");
updateWifiState(WifiManager.WIFI_STATE_UNKNOWN,
WifiManager.WIFI_STATE_ENABLED);
mStateMachine.sendMessage(CMD_INTERFACE_DOWN);
}
}
还以为androidP 直接抛弃了WifiStateMachine呢,原来这里才开始有交互。紧接着往下走吧
WifiStateMachine的初始状态为mDefaultState ,调用了setOperationalMode后
public void setOperationalMode(int mode, String ifaceName) {
if (mVerboseLoggingEnabled) {
log("setting operational mode to " + String.valueOf(mode) + " for iface: " + ifaceName);
}
mModeChange = true;
if (mode != CONNECT_MODE) {
// we are disabling client mode... need to exit connect mode now
transitionTo(mDefaultState);
} else {
// do a quick sanity check on the iface name, make sure it isn't null
if (ifaceName != null) {
mInterfaceName = ifaceName;
transitionTo(mDisconnectedState);
} else {
Log.e(TAG, "supposed to enter connect mode, but iface is null -> DefaultState");
transitionTo(mDefaultState);
}
}
// use the CMD_SET_OPERATIONAL_MODE to force the transitions before other messages are
// handled.
sendMessageAtFrontOfQueue(CMD_SET_OPERATIONAL_MODE);
}
会先跳转到DisconnectedState,然后再发送cmd CMD_SET_OPERATIONAL_MODE
class DisconnectedState extends State {
@Override
public void enter() {
Log.i(TAG, "disconnectedstate enter");
// We dont scan frequently if this is a temporary disconnect
// due to p2p
if (mTemporarilyDisconnectWifi) {
p2pSendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
return;
}
if (mVerboseLoggingEnabled) {
logd(" Enter DisconnectedState screenOn=" + mScreenOn);
}
/** clear the roaming state, if we were roaming, we failed */
mIsAutoRoaming = false;
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
mDisconnectedTimeStamp = mClock.getWallClockMillis();
}
/**
* Handler for WiFi state (connected/disconnected) changes
*/
public void handleConnectionStateChanged(int state) {
localLog("handleConnectionStateChanged: state=" + stateToString(state));
mWifiState = state;
if (mWifiState == WIFI_STATE_CONNECTED) {
mOpenNetworkNotifier.handleWifiConnected();
mCarrierNetworkNotifier.handleWifiConnected();
}
// Reset BSSID of last connection attempt and kick off
// the watchdog timer if entering disconnected state.
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mLastConnectionAttemptBssid = null;
scheduleWatchdogTimer();
startConnectivityScan(SCAN_IMMEDIATELY);
} else {
startConnectivityScan(SCAN_ON_SCHEDULE);
}
}
至此,framework开始作扫描动作。
扫描放到下一篇来说。