【安卓Framework学习】Wifi框架学习之核心类.
【安卓Framework学习】Wifi框架学习之开启与关闭流程.
【安卓Framework学习】Wifi框架学习之wifi状态机.
【安卓Framework学习】Wifi框架学习之扫描模式及扫描流程.
【安卓Framework学习】Wifi框架学习之热点评分机制.
【安卓Framework学习】安卓连接管理(ConnectivityService)之wifi连接及注册.
前几篇介绍了wifi框架中的状态机和wifi的开关流程,本篇将基于wifi的连接和断开功能分析其代码流程。由于连接和断开是wifi功能中用得最多也是最重要的功能之一,可以说前几篇都也为这一篇做了铺垫。本篇代码主要基于安卓11源码进行分析。
接上一篇【安卓Framework学习】Wifi框架学习之开启与关闭流程,从这一篇可以知道,在wifi被打开时,在ClientModeManager.ConnectModeState
中会将ClientModeImpl
的状态切换到DisconnectedState
,如下代码。
ClientModeManager.ConnectModeState
private class ConnectModeState extends State {
@Override
public void enter() {
Log.d(TAG, "entering ConnectModeState");
mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE,
mClientInterfaceName);
mModeListener.onStarted();
updateConnectModeState(WifiManager.WIFI_STATE_ENABLED,
WifiManager.WIFI_STATE_ENABLING);
// Inform sar manager that wifi is Enabled
mSarManager.setClientWifiState(WifiManager.WIFI_STATE_ENABLED);
}
/*省略其他代码*/
}
可以看到,在ConnectModeState.enter
中切换了ClientModeImpl
的状态,转到ClientModeImpl.setOperationalMode
方法中。
public void setOperationalMode(int mode, String ifaceName) {
/*省略其他代码*/
mModeChange = true;
if (mode != CONNECT_MODE) {
/*省略其他代码*/
} else {
if (ifaceName != null) {
mInterfaceName = ifaceName;
updateInterfaceCapabilities(ifaceName);
transitionTo(mDisconnectedState);
mWifiScoreReport.setInterfaceName(ifaceName);
} else {
/*省略其他代码*/
}
}
sendMessageAtFrontOfQueue(CMD_SET_OPERATIONAL_MODE);
}
由于已经是开启状态,wifi的接口名是有的,所以ifaceName
不为空,可以看到在setOperationalMode
方法中将状态切换到DisconnectedState
了。接下来将从应用调用连接某个指定AP开始分析。
在开发应用的过程中,都会用到WifiManager
,下面从WifiManager
调用连接wifi开始。代码如下。
public boolean enableNetwork(int netId, boolean attemptConnect) {
try {
return mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
这个方法,在安卓10的时候被弃用了,由于本人项目中使用的是安卓9,并且后续内部连接的过程都相差无几,所以这里还是从这个方法入手。转入到WifiServiceImpl
中的同名方法。
public boolean enableNetwork(int netId, boolean disableOthers, String packageName) {
/*省略其他代码*/
if (!isTargetSdkLessThanQOrPrivileged(packageName, Binder.getCallingPid(), Binder.getCallingUid())) {
mLog.info("enableNetwork not allowed for uid=%")
.c(Binder.getCallingUid()).flush();
return false;
}
int callingUid = Binder.getCallingUid();
/*省略其他代码*/
if (disableOthers) {
return triggerConnectAndReturnStatus(netId, callingUid);
} else {
return mWifiThreadRunner.call(
() -> mWifiConfigManager.enableNetwork(netId, false, callingUid, packageName),
false);
}
}
这个方法的disableOthers
表示是否需要让其他的热点配置信息不可用,一般情况下在使用的时候都会设置为true
,表示只能用指定的热点配置信息。转入到triggerConnectAndReturnStatus
中。
private boolean triggerConnectAndReturnStatus(int netId, int callingUid) {
/*省略其他代码*/
mClientModeImpl.connect(null, netId, new Binder(), connectListener,
connectListener.hashCode(), callingUid);
// now wait for response.
/*省略其他代码*/
}
直接调用到了ClientModeImpl.connect
方法中,转到connect
方法。
public void connect(WifiConfiguration config, int netId, @Nullable IBinder binder,
@Nullable IActionListener callback, int callbackIdentifier, int callingUid) {
mWifiInjector.getWifiThreadRunner().post(() -> {
/*省略其他代码*/
NetworkUpdateResult result = null;
if (config != null) {
/*省略其他代码*/
} else {
if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
loge("connectNetwork Invalid network Id=" + netId);
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
result = new NetworkUpdateResult(netId);
}
/*省略其他代码*/
Message message =
obtainMessage(CMD_CONNECT_NETWORK, -1, callbackIdentifier, result);
message.sendingUid = callingUid;
sendMessage(message);
});
}
connect
方法中,判断了需要连接的热点配置信息是否已经保存,然后就对状态机发送了CMD_CONNECT_NETWORK
消息。在本篇开始的时候提到了,在开启wifi后,ClientModeImpl
处于DisconnectedState
状态。转入DisconnectedState
状态,这里不再贴DisconnectedState.processMessage
方法的源码,因为DisconnectedState
状态没有对CMD_CONNECT_NETWORK
消息进行处理,而是继续交给了其父状态ConnectModeState
处理了,ConnectModeState.processMessage
源码如下。
public boolean processMessage(Message message) {
/*省略其他代码*/
switch (message.what) {
/*省略其他代码*/
case CMD_CONNECT_NETWORK:
callbackIdentifier = message.arg2;
result = (NetworkUpdateResult) message.obj;
netId = result.getNetworkId();
connectToUserSelectNetwork(
netId, message.sendingUid, result.hasCredentialChanged());
mWifiMetrics.logStaEvent(
StaEvent.TYPE_CONNECT_NETWORK,
mWifiConfigManager.getConfiguredNetwork(netId));
sendActionListenerSuccess(callbackIdentifier);
break;
/*省略其他代码*/
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
可以看到,ConnectModeState
在处理CMD_CONNECT_NETWORK
消息时,调用了connectToUserSelectNetwork
,在看connectToUserSelectNetwork
方法。
private void connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
/*省略其他代码*/
if (!forceReconnect && (mLastNetworkId == netId || mTargetNetworkId == netId)) {
/*省略其他代码*/
} else {
mWifiConnectivityManager.prepareForForcedConnection(netId);
if (uid == Process.SYSTEM_UID) {
mWifiMetrics.setNominatorForNetwork(netId,
WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
}
startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
}
}
由于我们分析的是刚打开wifi然后去链接,所以会进入到下面的分支,直接看startConnectToNetwork
方法。
public void startConnectToNetwork(int networkId, int uid, String bssid) {
sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
由于到这里还没有切换状态,所以还是ConnectModeState
处理CMD_START_CONNECT
消息。
public boolean processMessage(Message message) {
/*省略其他代码*/
switch (message.what) {
/*省略其他代码*/
case CMD_START_CONNECT:
/* connect command coming from auto-join */
netId = message.arg1;
int uid = message.arg2;
bssid = (String) message.obj;
mSentHLPs = false;
if (!hasConnectionRequests()) {
if (mNetworkAgent == null) {
loge("CMD_START_CONNECT but no requests and not connected,"
+ " bailing");
break;
} else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
loge("CMD_START_CONNECT but no requests and connected, but app "
+ "does not have sufficient permissions, bailing");
break;
}
}
config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
logd("CMD_START_CONNECT "
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
+ " roam=" + Boolean.toString(mIsAutoRoaming));
if (config == null) {
loge("CMD_START_CONNECT and no config, bail out...");
break;
}
mTargetNetworkId = netId;
// Update scorecard while there is still state from existing connection
int scanRssi = mWifiConfigManager.findScanRssi(netId,
mWifiHealthMonitor.getScanRssiValidTimeMs());
mWifiScoreCard.noteConnectionAttempt(mWifiInfo, scanRssi, config.SSID);
mBssidBlocklistMonitor.updateFirmwareRoamingConfiguration(config.SSID);
updateWifiConfigOnStartConnection(config, bssid);
reportConnectionAttemptStart(config, mTargetBssid,
WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
mWifiInfo.setMacAddress(currentMacAddress);
Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
mTargetWifiConfiguration = config;
/* Check for FILS configuration again after updating the config */
if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.FILS_SHA256)
|| config.allowedKeyManagement.get(
WifiConfiguration.KeyMgmt.FILS_SHA384)) {
boolean isIpClientStarted = startIpClient(config, true);
if (isIpClientStarted) {
mIpClientWithPreConnection = true;
break;
}
}
connectToNetwork(config);
break;
/*省略其他代码*/
default:
handleStatus = NOT_HANDLED;
break;
}
if (handleStatus == HANDLED) {
logStateAndMessage(message, this);
}
return handleStatus;
}
前面都是记录和配置校验连接信息,其实真正连接的就是最后调用的connectToNetwork
方法。
void connectToNetwork(WifiConfiguration config) {
if ((config != null) && mWifiNative.connectToNetwork(mInterfaceName, config)) {
mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime();
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
mLastConnectAttemptTimestamp = mClock.getWallClockMillis();
mIsAutoRoaming = false;
if (getCurrentState() != mDisconnectedState) {
transitionTo(mDisconnectingState);
}
} else {
loge("CMD_START_CONNECT Failed to start connection to network " + config);
mTargetWifiConfiguration = null;
stopIpClient();
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
}
}
由于在连接时,传入的参数肯定不会是空,所以连接是否成功就完全取决于WifiNative.connectToNetwork
方法是否成功,转入到WifiNative
中。
public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
// Abort ongoing scan before connect() to unblock connection request.
mWifiCondManager.abortScan(ifaceName);
return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);
}
这时候到了WifiNative
中基本上是属于与HAl层进行交互了,但这里还是继续分析下去,进入到SupplicantStaIfaceHal
中。
public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
synchronized (mLock) {
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
/*省略其他代码*/
} else {
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
if (!removeAllNetworks(ifaceName)) {
loge("Failed to remove existing networks");
return false;
}
Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
addNetworkAndSaveConfig(ifaceName, config);
if (pair == null) {
loge("Failed to add/save network configuration: " + config.getKey());
return false;
}
mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
}
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
if (networkHandle == null) {
loge("No valid remote network handle for network configuration: "
+ config.getKey());
return false;
}
PmkCacheStoreData pmkData = mPmkCacheEntries.get(config.networkId);
if (pmkData != null
&& !WifiConfigurationUtil.isConfigForPskNetwork(config)
&& pmkData.expirationTimeInSec > mClock.getElapsedSinceBootMillis() / 1000) {
logi("Set PMK cache for config id " + config.networkId);
if (networkHandle.setPmkCache(pmkData.data)) {
mWifiMetrics.setConnectionPmkCache(true);
}
}
if (!networkHandle.select()) {
loge("Failed to select network configuration: " + config.getKey());
return false;
}
return true;
}
}
上述代码中,不针对每个方法进行分析,有些会带过,需要的可自行查看。首先传进来的连接配置信息肯定是没连接过的,所以直接走到下面的分支。开始会移除掉本地缓存Iface
对应的SupplicantStaNetworkHal
对象和Iface
对应的的WifiConfiguration
对象,然后通过addNetworkAndSaveConfig
方法获得新的SupplicantStaNetworkHal
对象并和WifiConfiguration
生成一个Pair
,再看addNetworkAndSaveConfig
.
private Pair<SupplicantStaNetworkHal, WifiConfiguration>
addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
synchronized (mLock) {
logi("addSupplicantStaNetwork via HIDL");
if (config == null) {
loge("Cannot add NULL network!");
return null;
}
SupplicantStaNetworkHal network = addNetwork(ifaceName);
if (network == null) {
loge("Failed to add a network!");
return null;
}
boolean saveSuccess = false;
try {
saveSuccess = network.saveWifiConfiguration(config);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Exception while saving config params: " + config, e);
}
/*省略其他代码*/
return new Pair(network, new WifiConfiguration(config));
}
}
上述代码中addNetwork
其实就是从java进程缓存中获取一个SupplicantStaNetworkHal
对象,然后通过saveWifiConfiguration
方法将要连接的配置信息通过HIDL设置保存到HAL层中。再次看connectToNetwork
方法。在获得SupplicantStaNetworkHal
对象后,并且此时也保存了要连接的配置信息,然后调用了SupplicantStaNetworkHal .select
方法通知HAL层通过保存的配置信息去连接对应的热点。后续实际连接的操作就都在HAL层及驱动层了,Framework中java部分就到这里结束了,后续等HAL层通过调用java层的回调函数通知安卓系统具体的连接过程。现在看是如何将连接过程的广播发送上来的。部分代码不做往上分析,直接先用结果,在开启wifi的时候,WifiNative
中会调用SupplicantStaIfaceHal.setupIface
方法。
public boolean setupIface(@NonNull String ifaceName) {
final String methodStr = "setupIface";
if (checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr) != null) return false;
ISupplicantIface ifaceHwBinder;
if (isV1_1()) {
ifaceHwBinder = addIfaceV1_1(ifaceName);
} else {
ifaceHwBinder = getIfaceV1_0(ifaceName);
}
if (ifaceHwBinder == null) {
Log.e(TAG, "setupIface got null iface");
return false;
}
try {
ISupplicantStaIface iface = setupStaIface(ifaceName, ifaceHwBinder);
mISupplicantStaIfaces.put(ifaceName, iface);
} catch (RemoteException e) {
loge("setup StaIface failed: " + e.toString());
return false;
}
return true;
}
方法中先会用addIfaceV1_1
或addIfaceV1_0
方法来获得一个ISupplicantIface
的Binder对象,至于这里怎么获得的在本篇不做详细分析,感兴趣的可以自行研究,然后将继续调用setupStaIface
方法。
private ISupplicantStaIface setupStaIface(@NonNull String ifaceName,
@NonNull ISupplicantIface ifaceHwBinder) throws RemoteException {
/* Prepare base type for later cast. */
ISupplicantStaIface iface = getStaIfaceMockable(ifaceHwBinder);
/* try newer version first. */
if (trySetupStaIfaceV1_1(ifaceName, iface)) {
logd("Newer HAL is found, skip V1_0 remaining init flow.");
return iface;
}
SupplicantStaIfaceHalCallback callback = new SupplicantStaIfaceHalCallback(ifaceName);
if (!registerCallback(iface, callback)) {
throw new RemoteException("Init StaIface V1_0 failed.");
}
/* keep this in a store to avoid recycling by garbage collector. */
mISupplicantStaIfaceCallbacks.put(ifaceName, callback);
return iface;
}
在setupStaIface
会先调用getStaIfaceMockable
将ISupplicantIface
的对象转换成ISupplicantStaIface
对象,然后实例化了一个SupplicantStaIfaceHalCallback
对象,通过registerCallback
方法将SupplicantStaIfaceHalCallback
对象注册到ISupplicantStaIface
当中,这时候HAL层就拥有了java层的回调对象,继续看SupplicantStaIfaceHalCallback
中的onStateChanged
回调方法。
public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
ArrayList<Byte> ssid) {
synchronized (mLock) {
mStaIfaceHal.logCallback("onStateChanged");
SupplicantState newSupplicantState =
supplicantHidlStateToFrameworkState(newState);
WifiSsid wifiSsid =
WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE);
if (newSupplicantState == SupplicantState.COMPLETED) {
mWifiMonitor.broadcastNetworkConnectionEvent(
mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), bssidStr);
}
mWifiMonitor.broadcastSupplicantStateChangeEvent(
mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), wifiSsid,
bssidStr, newSupplicantState);
}
}
在supplicantHidlStateToFrameworkState
方法中,将HAL层传上来的状态转换成SupplicantState
,然后通过WifiMonitor
将相关信息通过广播发送出去。这里看WifiMonitor.broadcastSupplicantStateChangeEvent
.
public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
String bssid,
SupplicantState newSupplicantState) {
sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
}
在方法中,直接对外发送了一个SUPPLICANT_STATE_CHANGE_EVENT
消息,这个消息是交给通过registerHandler
方法注册过的Handler去处理的。而在ClientModeImpl.registerForWifiMonitorEvents
方法中有注册Handler对象到WifiMonitor
中,而registerForWifiMonitorEvents
方法在ConnectModeState.enter
中会被调用到。文章开始时说到,在开启成功后ClientModeImpl
会被切换到DisconnectedState
,ConnectModeState
是DisconnectedState
的父状态,所以ConnectModeState.enter
方法在开启wifi成功时就被调用了。这时ClientModeImpl
处于DisconnectedState
状态,再看DisconnectedState
状态对消息SUPPLICANT_STATE_CHANGE_EVENT
是如何处理的。
public boolean processMessage(Message message) {
switch (message.what) {
/*省略其他代码*/
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
/*省略其他代码*/
if (SupplicantState.isConnecting(stateChangeResult.state)) {
WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(
stateChangeResult.networkId);
// Update Passpoint information before setNetworkDetailedState as
// WifiTracker monitors NETWORK_STATE_CHANGED_ACTION to update UI.
mWifiInfo.setFQDN(null);
mWifiInfo.setPasspointUniqueId(null);
mWifiInfo.setOsuAp(false);
mWifiInfo.setProviderFriendlyName(null);
if (config != null && (config.isPasspoint() || config.osu)) {
if (config.isPasspoint()) {
mWifiInfo.setFQDN(config.FQDN);
mWifiInfo.setPasspointUniqueId(config.getPasspointUniqueId());
} else {
mWifiInfo.setOsuAp(true);
}
mWifiInfo.setProviderFriendlyName(config.providerFriendlyName);
}
}
sendNetworkChangeBroadcast(
WifiInfo.getDetailedStateOf(stateChangeResult.state));
/* ConnectModeState does the rest of the handling */
handleStatus = NOT_HANDLED;
break;
/*省略其他代码*/
}
}
在DisconnectedState
状态处理消息SUPPLICANT_STATE_CHANGE_EVENT
时,最后调用了sendNetworkChangeBroadcast
将状态通过广播(WifiManager.NETWORK_STATE_CHANGED_ACTION
)发送出去,sendNetworkChangeBroadcast
方法不再赘述,里面就是正常发广播的操作。再次回到onStateChanged
回调方法中,当最新的状态newSupplicantState == SupplicantState.COMPLETED
时,也就是连接完成时,会调用WifiMonitor.broadcastNetworkConnectionEvent
方法,会发送一个NETWORK_CONNECTION_EVENT
消息,这时ClientModeImpl
仍处于DisconnectedState
状态。
public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
}
由于DisconnectedState
状态没有对NETWORK_CONNECTION_EVENT
消息进行处理,将交由其父状态ConnectModeState
进行处理。
public boolean processMessage(Message message) {
switch (message.what) {
/*省略其他代码*/
case WifiMonitor.NETWORK_CONNECTION_EVENT:
/*省略其他代码*/
if (config != null) {
/*省略其他代码*/
transitionTo(mObtainingIpState);
} else {
/*省略其他代码*/
}
break;
/*省略其他代码*/
}
}
其他代码都省略了,值看最后的切换状态到ObtainingIpState
,那么根据之前说的状态机的工作机制,会对新状态树新的状态调用enter
方法,先看ObtainingIpState.enter
.
public void enter() {
WifiConfiguration currentConfig = getCurrentWifiConfiguration();
if (mIpClientWithPreConnection && mIpClient != null) {
mIpClient.notifyPreconnectionComplete(mSentHLPs);
mIpClientWithPreConnection = false;
mSentHLPs = false;
} else {
startIpClient(currentConfig, false);
}
// Get Link layer stats so as we get fresh tx packet counters
getWifiLinkLayerStats();
}
假设之前没有进行预连接或预连接不成功,那么会进入第二个分支,调用startIpClient
.
private boolean startIpClient(WifiConfiguration config, boolean isFilsConnection) {
/*省略部分代码*/
if (isFilsConnection) {
/*省略部分代码*/
mIpClient.startProvisioning(prov.build());
} else {
sendNetworkChangeBroadcast(DetailedState.OBTAINING_IPADDR);
/*省略部分代码*/
mIpClient.startProvisioning(prov.build());
}
}
由于传入的isFilsConnection
是false
那么会进入下面的分支,会先发送DetailedState.OBTAINING_IPADDR
状态的广播,然后再调用startProvisioning
方法根据提供的配置参数进行网络配置。然后再看L2ConnectedState.enter
.
public void enter() {
mRssiPollToken++;
if (mEnableRssiPolling) {
mLinkProbeManager.resetOnNewConnection();
sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
}
sendNetworkChangeBroadcast(DetailedState.CONNECTING);
/*省略其他代码*/
}
在L2ConnectedState.enter
中,又调用了sendNetworkChangeBroadcast
方法,并且发送的是DetailedState.CONNECTING
状态。由于在ObtainingIpState.enter
调用了startProvisioning
进行配置,这里在配置成功后会调用ClientModeImpl.IpClientCallbacksImpl
中的onProvisioningSuccess
方法。
public void onProvisioningSuccess(LinkProperties newLp) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
}
CMD_UPDATE_LINKPROPERTIES
消息为更新连接配置,由于是刚连接不会发送广播,那么直接看CMD_IP_CONFIGURATION_SUCCESSFUL
消息的处理,这时还是在ObtainingIpState
的状态树下,因为ObtainingIpState
没有对此消息进行处理,所以直接看其父状态L2ConnectedState
的处理过程。
public boolean processMessage(Message message) {
/*省略其他代码*/
switch (message.what) {
/*省略其他代码*/
case CMD_IP_CONFIGURATION_SUCCESSFUL:
if (getCurrentWifiConfiguration() == null) {
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
WifiMetricsProto.ConnectionEvent.HLF_NONE,
WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
} else {
handleSuccessfulIpConfiguration();
sendConnectedState();
transitionTo(mConnectedState);
}
break;
/*省略其他代码*/
}
/*省略其他代码*/
}
由于是连接过程,配置信息不为空,会进入下面的分支,这时候会调用sendConnectedState
方法发送广播,对外发送了DetailedState.CONNECTED
广播,表示成功连接上了某个热点,并且将状态切换到了ConnectedState
。
private void sendConnectedState() {
mNetworkAgent.markConnected();
sendNetworkChangeBroadcast(DetailedState.CONNECTED);
}
前面一部分分析了wifi开启后连接一个热点到连接成功的过程,本小结分析断开流程就相对要简单一些。仍然从用户点击断开调用WifiManager.disconnect
方法开始。
public boolean disconnect() {
try {
return mService.disconnect(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
转入到WifiServiceImpl.disconnect
中。
public boolean disconnect(String packageName) {
/*省略其他代码*/
mClientModeImpl.disconnectCommand();
return true;
}
直接调用了ClientModeImpl.disconnectCommand
中。
public void disconnectCommand() {
sendMessage(CMD_DISCONNECT);
}
这里直接发送了CMD_DISCONNECT
消息给状态机。由于上部分分析了,在连接成功后将状态机ClientModeImpl
的状态切换到了ConnectedState
,由于ConnectedState
对消息CMD_DISCONNECT
没有处理,将交给其父状态L2ConnectedState
处理此消息。
public boolean processMessage(Message message) {
/*省略其他代码*/
switch (message.what) {
/*省略其他代码*/
case CMD_DISCONNECT:
mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
StaEvent.DISCONNECT_GENERIC);
mWifiNative.disconnect(mInterfaceName);
transitionTo(mDisconnectingState);
break;
/*省略其他代码*/
}
/*省略其他代码*/
}
在L2ConnectedState
中,直接调用了WifiNative.disconnect
方法断开当前连接的热点,然后将状态机切换到DisconnectingState
状态。在看WifiNative.disconnect
.
WifiNative.java
public boolean disconnect(@NonNull String ifaceName) {
return mSupplicantStaIfaceHal.disconnect(ifaceName);
}
SupplicantStaIfaceHal.java
public boolean disconnect(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "disconnect";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return false;
try {
SupplicantStatus status = iface.disconnect();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
可以看到WifiNative
中直接调用了SupplicantStaIfaceHal.disconnect
,通过HIDL调用了HAL层中的disconnect
方法。由于在开启wifi时,向ISupplicantStaIface
注册了回调类SupplicantStaIfaceHalCallback
其父类为SupplicantStaIfaceCallbackImpl
,再看SupplicantStaIfaceCallbackImpl
的回调方法onDisconnect
.
public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
synchronized (mLock) {
/*省略其他代码*/
mWifiMonitor.broadcastNetworkDisconnectionEvent(
mIfaceName, locallyGenerated ? 1 : 0, reasonCode,
NativeUtil.macAddressFromByteArray(bssid));
}
}
再看broadcastNetworkDisconnectionEvent
方法。
public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
String bssid) {
sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
}
这里之前讲到过WifiMonitor
,会将各个类的Handler对象注册到其中。所以这里其实是给ClientModeImpl
发送消息,由于回调是跨进程的,所以会排在当前消息后面。再回到L2ConnectedState
处理CMD_DISCONNECT
消息,会将状态切换到DisconnectingState
,那么broadcastNetworkDisconnectionEvent
方法发送的NETWORK_DISCONNECTION_EVENT
是交给DisconnectingState
状态处理了。由于DisconnectingState
状态没有处理,会交由其父状态ConnectModeState
处理。
public boolean processMessage(Message message) {
/*省略其他代码*/
switch (message.what) {
/*省略其他代码*/
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (mVerboseLoggingEnabled) log("ConnectModeState: Network connection lost ");
clearNetworkCachedDataIfNeeded(getTargetWifiConfiguration(), message.arg2);
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
break;
/*省略其他代码*/
}
/*省略其他代码*/
}
这里只看handleNetworkDisconnect
方法即可,因为其他方法对关闭流程没很大影响了。
private void handleNetworkDisconnect() {
/*省略其他代码*/
sendNetworkChangeBroadcast(DetailedState.DISCONNECTED);
/*省略其他代码*/
}
这里对外发送了wifi状态变化的广播,并且状态为DetailedState.DISCONNECTED
,这个广播发送出去后表示wifi完全断开热点成功。
相比起来,wifi连接的过程要复杂得很多,中间发送的连接状态广播也很多。其中主要还是通过开启时通过HIDL获得的ISupplicantStaIface
对象对下面的连接断开添加网络请求等操作,所以后续需要继续跟进需要学习了解HIDL,其实是和AIDL类似的跨进程通信,有需要可以深入研究。