梳理一下Android11的wifi连接流程。
一、可以看到点击连接以后,如果config不为null,则先保存网络,再进行连接,所以即使连接失败,此网络依然在已保存网络列表里。
packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java
void submit(WifiConfigController configController) {
final WifiConfiguration config = configController.getConfig();
if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
if(!configController.checkWapiParam()) {
if(configController.getCurSecurity() == AccessPoint.SECURITY_WAPI_CERT) {
startWapiCertManage();
}
return;
}
mWifiManager.save(config, mSaveListener);
} else {
if(!configController.checkWapiParam()) {
if(configController.getCurSecurity() == AccessPoint.SECURITY_WAPI_CERT) {
startWapiCertManage();
}
return;
}
mWifiManager.save(config, mSaveListener);
if (mSelectedAccessPoint != null) { // Not an "Add network"
connect(config, false /* isSavedNetwork */,
CONNECT_SOURCE_UNSPECIFIED);
}
}
mWifiTracker.resumeScanning();
}
protected void connect(final WifiConfiguration config,
boolean isSavedNetwork, @ConnectSource int connectSource) {
// Log subtype if configuration is a saved network.
mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_WIFI_CONNECT,
isSavedNetwork);
mConnectSource = connectSource;
mWifiManager.connect(config, mConnectListener);
mClickedConnect = true;
}
二、这里我们先看connect是怎么实现的,save的过程最后再看。具体实现还是在service,wifimanager只是一个桥梁、
frameworks/base/wifi/java/android/net/wifi/WifiManager.java
public void connect(@NonNull WifiConfiguration config, @Nullable ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
connectInternal(config, WifiConfiguration.INVALID_NETWORK_ID, listener);
}
private void connectInternal(@Nullable WifiConfiguration config, int networkId,
@Nullable ActionListener listener) {
ActionListenerProxy listenerProxy = null;
Binder binder = null;
if (listener != null) {
listenerProxy = new ActionListenerProxy("connect", mLooper, listener);
binder = new Binder();
}
try {
mService.connect(config, networkId, binder, listenerProxy,
listener == null ? 0 : listener.hashCode());
} catch (RemoteException e) {
if (listenerProxy != null) listenerProxy.onFailure(ERROR);
} catch (SecurityException e) {
if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED);
}
}
三、wifiservice会判断uid的权限,然后这里会判断staid,因为android11上层是支持了双wifi的,就是连接俩个AP,当然具体功能还要厂商自己实现,双AP具体可以看Android11 wifi开启流程,这里开启wifi时就会分配staid。这里如果是AP1则是正常流程走ClientModeImpl,如果是AP2则会走QtiClientModeImpl。
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
public void connect(WifiConfiguration config, int netId, IBinder binder,
@Nullable IActionListener callback, int callbackIdentifier) {
int uid = Binder.getCallingUid();
if (!isPrivileged(Binder.getCallingPid(), uid)) {
throw new SecurityException(TAG + ": Permission denied");
}
mLog.info("connect uid=%").c(uid).flush();
int staId;
if(config != null) staId = config.staId;
else staId = getIdentityForNetwork(netId);
if(staId == STA_PRIMARY) {
} else {
QtiClientModeImpl qtiClientModeImpl = mActiveModeWarden.getQtiClientModeImpl(staId);
if (qtiClientModeImpl != null)
qtiClientModeImpl.connect(config, netId, binder, callback, callbackIdentifier, uid);
}
if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
if (config == null) {
mWifiMetrics.logUserActionEvent(UserActionEvent.EVENT_MANUAL_CONNECT, netId);
} else {
mWifiMetrics.logUserActionEvent(
UserActionEvent.EVENT_ADD_OR_UPDATE_NETWORK, config.networkId);
}
}
mClientModeImpl.connect(config, netId, binder, callback, callbackIdentifier, uid);
}
四、看一下wifi状态机里做了什么事情。
首先是调用WifiConfigManager.addOrUpdateNetwork来更新网络配置。如果保存成功则发送广播。然后检查网络权限等等各项操作结束以后,发送消息CMD_CONNECT_NETWORK。
frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
public void connect(WifiConfiguration config, int netId, @Nullable IBinder binder,
@Nullable IActionListener callback, int callbackIdentifier, int callingUid) {
mWifiInjector.getWifiThreadRunner().post(() -> {
if (callback != null && binder != null) {
mProcessingActionListeners.add(binder, callback, callbackIdentifier);
}
/**
* The connect message can contain a network id passed as arg1 on message or
* or a config passed as obj on message.
* For a new network, a config is passed to create and connect.
* For an existing network, a network id is passed
*/
NetworkUpdateResult result = null;
if (config != null) {
result = mWifiConfigManager.addOrUpdateNetwork(config, callingUid);
if (!result.isSuccess()) {
loge("connectNetwork adding/updating config=" + config + " failed");
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
} else {
if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
loge("connectNetwork Invalid network Id=" + netId);
sendActionListenerFailure(callbackIdentifier, WifiManager.ERROR);
return;
}
result = new NetworkUpdateResult(netId);
}
final int networkId = result.getNetworkId();
mWifiConfigManager.userEnabledNetwork(networkId);
if (!mWifiConfigManager.enableNetwork(networkId, true, callingUid, null)
|| !mWifiConfigManager.updateLastConnectUid(networkId, callingUid)) {
logi("connect Allowing uid " + callingUid
+ " with insufficient permissions to connect=" + networkId);
} else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(callingUid)) {
// Note user connect choice here, so that it will be considered in the
// next network selection.
mWifiConnectivityManager.setUserConnectChoice(networkId);
}
Message message =
obtainMessage(CMD_CONNECT_NETWORK, -1, callbackIdentifier, result);
message.sendingUid = callingUid;
sendMessage(message);
});
}
现在wifi状态机应该在ConnectModeState。我们看它怎么处理。
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;
private void connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
+ ", forceReconnect = " + forceReconnect);
if (!forceReconnect && (mLastNetworkId == netId || mTargetNetworkId == netId)) {
// We're already connecting/connected to the user specified network, don't trigger a
// reconnection unless it was forced.
logi("connectToUserSelectNetwork already connecting/connected=" + netId);
} else {
mWifiConnectivityManager.prepareForForcedConnection(netId);
if (uid == Process.SYSTEM_UID) {
mWifiMetrics.setNominatorForNetwork(netId,
WifiMetricsProto.ConnectionEvent.NOMINATOR_MANUAL);
}
startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
}
}
这里有发送了CMD_START_CONNECT消息。
public void startConnectToNetwork(int networkId, int uid, String bssid) {
sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
还是在ConnectModeState中处理。在这里会更新AP的信息,然后计分器打分,从底层获取macaddress,然后开启IPClient。上述完成以后开始connectToNetwork
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;
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到了SupplicantStaIfaceHal
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
connectToNetwork->addNetworkAndSaveConfig->addNetwork->supplicant
六、到了supplicant里面,添加网络,注册网络,完成以后就要开始连接了
external/wpa_supplicant_8/wpa_supplicant/hidl/1.3/sta_iface.cpp
std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
StaIface::addNetworkInternal()
{
android::sp<ISupplicantStaNetwork> network;
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
struct wpa_ssid *ssid = wpa_supplicant_add_network(wpa_s);
if (!ssid) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
}
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager ||
hidl_manager->getStaNetworkHidlObjectByIfnameAndNetworkId(
wpa_s->ifname, ssid->id, &network)) {
return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
}
return {{SupplicantStatusCode::SUCCESS, ""}, network};
}
external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.c
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
ssid = wpa_config_add_network(wpa_s->conf);
if (!ssid)
return NULL;
wpas_notify_network_added(wpa_s, ssid);
ssid->disabled = 1;
wpa_config_set_network_defaults(ssid);
return ssid;
}
/external/wpa_supplicant_8/wpa_supplicant/config.c
struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
{
int id;
struct wpa_ssid *ssid, *last = NULL;
id = -1;
ssid = config->ssid;
while (ssid) {
if (ssid->id > id)
id = ssid->id;
last = ssid;
ssid = ssid->next;
}
id++;
ssid = os_zalloc(sizeof(*ssid));
if (ssid == NULL)
return NULL;
ssid->id = id;
dl_list_init(&ssid->psk_list);
if (last)
last->next = ssid;
else
config->ssid = ssid;
wpa_config_update_prio_list(config);
return ssid;
}
external/wpa_supplicant_8/wpa_supplicant/notify.c
void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_mgmt)
return;
/*
* Networks objects created during any P2P activities should not be
* exposed out. They might/will confuse certain non-P2P aware
* applications since these network objects won't behave like
* regular ones.
*/
if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) {
wpas_dbus_register_network(wpa_s, ssid);
#ifdef CONFIG_HIDL
wpas_hidl_register_network(wpa_s, ssid);
#endif
}
}
external/wpa_supplicant_8/wpa_supplicant/hidl/1.3/hidl.cpp
int wpas_hidl_register_network(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
if (!wpa_s || !wpa_s->global->hidl || !ssid)
return 1;
wpa_printf(
MSG_DEBUG, "Registering network to hidl control: %d", ssid->id);
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager)
return 1;
return hidl_manager->registerNetwork(wpa_s, ssid);
}
七、接着第五步,SupplicantStaIfaceHal中的connectToNetwork最后会执行select,我们看supplicant中select具体做了什么
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;
}
八、这里选择AP以后就开始关联了,关联成功就是四次握手。
external/wpa_supplicant_8/wpa_supplicant/hidl/1.3/sta_network.cpp
external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.c
/external/wpa_supplicant_8/wpa_supplicant/events.c
external/wpa_supplicant_8/src/rsn_supp/wpa.c
select->selectInternal->wpa_supplicant_select_network->wpa_supplicant_fast_associate->wpas_select_network_from_last_scan->wpa_supplicant_pick_network->wpa_supplicant_select_bss->wpa_supplicant_connect->wpa_supplicant_associate->wpas_start_assoc_cb->wpa_sm_set_assoc_wpa_ie
supplicant状态的关键日志
09-07 11:17:32.502 3911 3911 D wpa_supplicant: wlan0: State: DISCONNECTED -> ASSOCIATING
09-07 11:17:32.554 3911 3911 D wpa_supplicant: wlan0: State: ASSOCIATING -> ASSOCIATED
09-07 11:17:32.665 3911 3911 D wpa_supplicant: wlan0: State: ASSOCIATED -> 4WAY_HANDSHAKE
09-07 11:17:32.683 3911 3911 D wpa_supplicant: wlan0: State: 4WAY_HANDSHAKE -> 4WAY_HANDSHAKE
09-07 11:17:32.686 3911 3911 D wpa_supplicant: wlan0: State: 4WAY_HANDSHAKE -> GROUP_HANDSHAKE
09-07 11:17:32.689 3911 3911 D wpa_supplicant: wlan0: State: GROUP_HANDSHAKE -> COMPLETED
总体流程如下图,第一次画流程图,有点丑。