最近发现一些settings调用wifimanager连接wifi后,没有调用到wpa_supplicant的问题,借此梳理下Android wifi连接流程。
注:代码基于Android R(11) qualcomm平台
下面主要梳理从settings app到wpa的流程,闲话不多说,先上流程图。
Wifi 连接的交互界面位于settings app,一切调用都是从他开始,因此我们先从settings代码进行跟踪。
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();
}
可以看到点击连接以后,如果config不为null,则先保存网络,再进行连接,所以即使连接失败,此网络依然在已保存网络列表里。
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;
}
frameworks/base/wifi/java/android/net/wifi/WifiManager.java
这里我们先看connect是怎么实现的,save的过程最后再看。具体实现还是在service,wifimanager只是一个桥梁。
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);
}
}
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
/**
* see {@link android.net.wifi.WifiManager#connect(int, WifiManager.ActionListener)}
*/
@Override
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) {
mClientModeImpl.connect(config, netId, binder, callback, callbackIdentifier, uid);
} else {
QtiClientModeImpl qtiClientModeImpl = mWifiThreadRunner.call(() ->
mActiveModeWarden.getQtiClientModeImpl(), null);
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);
}
wifiservice会判断uid的权限,然后这里会判断staid,因为android11上层是支持了双wifi的,就是连接俩个AP,当然具体功能还要厂商自己实现,这里开启wifi时就会分配staid。这里如果是AP1则是正常流程走ClientModeImpl,如果是AP2则会走QtiClientModeImpl,我们先只跟踪AP1。
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状态机里做了什么事情。
首先是调用WifiConfigManager.addOrUpdateNetwork来更新网络配置。如果保存成功则发送广播。然后检查网络权限等等各项操作结束以后,发送消息CMD_CONNECT_NETWORK。
现在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;
这里面主要跟踪connectToUserSelectNetwork
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);
}
}
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
/**
* Add the provided network configuration to wpa_supplicant and initiate connection to it.
* This method does the following:
* 1. Abort any ongoing scan to unblock the connection request.
* 2. Remove any existing network in wpa_supplicant(This implicitly triggers disconnect).
* 3. Add a new network to wpa_supplicant.
* 4. Save the provided configuration to wpa_supplicant.
* 5. Select the new network in wpa_supplicant.
* 6. Triggers reconnect command to wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param configuration WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
// Abort ongoing scan before connect() to unblock connection request.
mWifiCondManager.abortScan(ifaceName);
if (configuration.staId == STA_PRIMARY && mIfaceBands.containsKey(STA_SECONDARY)) {
String SecStaifaceName = getSecondaryStaInterfaceName();
if (SecStaifaceName != null) {
Log.d(TAG, "Disconnect STA2 for STA1 connection");
disconnect(SecStaifaceName);
}
}
return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);
}
我们可以看到connectToNetwork通过WifiNative到了SupplicantStaIfaceHal
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
/**
* Add the provided network configuration to wpa_supplicant and initiate connection to it.
* This method does the following:
* 1. If |config| is different to the current supplicant network, removes all supplicant
* networks and saves |config|.
* 2. Select the new network in wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param config WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
synchronized (mLock) {
logd("connectToNetwork " + config.getKey());
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
String networkSelectionBSSID = config.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
String networkSelectionBSSIDCurrent =
currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
logd("Network is already saved, will not trigger remove and add operation.");
} else {
logd("Network is already saved, but need to update BSSID.");
if (!setCurrentNetworkBssid(
ifaceName,
config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
loge("Failed to set current network BSSID.");
return false;
}
mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
}
} 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()) { //1.8 -> sta_network.cpp
loge("Failed to select network configuration: " + config.getKey());
return false;
}
return true;
}
}
其中我们需要关注addNetworkAndSaveConfig
/**
* Add a network configuration to wpa_supplicant.
*
* @param config Config corresponding to the network.
* @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects
* for the current network.
*/
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);
}
if (!saveSuccess) {
loge("Failed to save variables for: " + config.getKey());
if (!removeAllNetworks(ifaceName)) {
loge("Failed to remove all networks on failure.");
}
return null;
}
return new Pair(network, new WifiConfiguration(config));
}
}
connectToNetwork -> addNetworkAndSaveConfig -> addNetwork -> supplicant
/**
* Adds a new network.
*
* @return The ISupplicantNetwork object for the new network, or null if the call fails
*/
private SupplicantStaNetworkHal addNetwork(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "addNetwork";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
try {
iface.addNetwork((SupplicantStatus status,
ISupplicantNetwork network) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
newNetwork.value = network; // 1.7 -> wpa_supplicant
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
if (newNetwork.value != null) {
return getStaNetworkMockable(
ifaceName,
ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
} else {
return null;
}
}
}
在上一节中我们看到调用了 iface.addNetwork
,跟踪下去会发现这是 wpa_supplicant 中 hidl 接口
external/wpa_supplicant_8/wpa_supplicant/hidl/1.3/sta_iface.cpp
Return<void> StaIface::addNetwork(addNetwork_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
&StaIface::addNetworkInternal, _hidl_cb);
}
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
/**
* wpa_supplicant_add_network - Add a new network
* @wpa_s: wpa_supplicant structure for a network interface
* Returns: The new network configuration or %NULL if operation failed
*
* This function performs the following operations:
* 1. Adds a new network.
* 2. Send network addition notification.
* 3. Marks the network disabled.
* 4. Set network default parameters.
*/
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
ssid = wpa_config_add_network(wpa_s->conf);//config.c
if (!ssid)
return NULL;
wpas_notify_network_added(wpa_s, ssid);//notify.c
ssid->disabled = 1;
wpa_config_set_network_defaults(ssid);
return ssid;
}
external/wpa_supplicant_8/wpa_supplicant/config.c
/**
* wpa_config_add_network - Add a new network with empty configuration
* @config: Configuration data from wpa_config_read()
* Returns: The new network configuration or %NULL if operation failed
*/
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);
wpas_hidl_register_network(wpa_s, ssid);
}
}
这里看到了执行了 wpas_dbus_register_network
和 wpas_hidl_register_network
但是通过查看log,我们发现,貌似最终执行下去的是wpas_hidl_register_network
,先继续跟踪,后续添加打印确认wpas_dbus_register_network
是否在执行过程中便return了
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);
}
external/wpa_supplicant_8/wpa_supplicant/hidl/1.3/hidl_manager.cpp
/**
* Register a network to hidl manager.
*
* @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
* the network is added.
* @param ssid |wpa_ssid| struct corresponding to the network being added.
*
* @return 0 on success, 1 on failure.
*/
int HidlManager::registerNetwork(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
if (!wpa_s || !ssid)
return 1;
// Generate the key to be used to lookup the network.
const std::string network_key =
getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
if (isP2pIface(wpa_s)) {
if (addHidlObjectToMap<P2pNetwork>(
network_key,
new P2pNetwork(wpa_s->global, wpa_s->ifname, ssid->id),
p2p_network_object_map_)) {
wpa_printf(
MSG_ERROR,
"Failed to register P2P network with HIDL "
"control: %d",
ssid->id);
return 1;
}
p2p_network_callbacks_map_[network_key] =
std::vector<android::sp<ISupplicantP2pNetworkCallback>>();
// Invoke the |onNetworkAdded| method on all registered
// callbacks.
callWithEachP2pIfaceCallback(
wpa_s->ifname,
std::bind(
&ISupplicantP2pIfaceCallback::onNetworkAdded,
std::placeholders::_1, ssid->id));
} else {
if (addHidlObjectToMap<StaNetwork>(
network_key,
new StaNetwork(wpa_s->global, wpa_s->ifname, ssid->id),
sta_network_object_map_)) {
wpa_printf(
MSG_ERROR,
"Failed to register STA network with HIDL "
"control: %d",
ssid->id);
return 1;
}
sta_network_callbacks_map_[network_key] =
std::vector<android::sp<ISupplicantStaNetworkCallback>>();
// Invoke the |onNetworkAdded| method on all registered
// callbacks.
callWithEachStaIfaceCallback(
wpa_s->ifname,
std::bind(
&ISupplicantStaIfaceCallback::onNetworkAdded,
std::placeholders::_1, ssid->id));
}
return 0;
}
接着我们回到1.6
节中,SupplicantStaIfaceHal中的connectToNetwork最后会执行select,我们看supplicant中select具体做了什么
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
/**
* Trigger a connection to this network.
*
* @return true if it succeeds, false otherwise.
*/
public boolean select() {
synchronized (mLock) {
final String methodStr = "select";
if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
try {
SupplicantStatus status = mISupplicantStaNetwork.select();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
这里选择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
external/wpa_supplicant_8/wpa_supplicant/hidl/1.3/sta_network.cpp
Return<void> StaNetwork::select(select_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
&StaNetwork::selectInternal, _hidl_cb);
}
SupplicantStatus StaNetwork::selectInternal()
{
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
if (wpa_ssid->disabled == 2) {
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
wpa_s->scan_min_time.sec = 0;
wpa_s->scan_min_time.usec = 0;
wpa_supplicant_select_network(wpa_s, wpa_ssid);
return {SupplicantStatusCode::SUCCESS, ""};
}
/external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.c
/**
* wpa_supplicant_select_network - Attempt association with a network
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network or %NULL for any network
*/
void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_ssid *other_ssid;
int disconnected = 0;
if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
disconnected = 1;
}
if (ssid)
wpas_clear_temp_disabled(wpa_s, ssid, 1);
/*
* Mark all other networks disabled or mark all networks enabled if no
* network specified.
*/
for (other_ssid = wpa_s->conf->ssid; other_ssid;
other_ssid = other_ssid->next) {
int was_disabled = other_ssid->disabled;
if (was_disabled == 2)
continue; /* do not change persistent P2P group data */
other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
if (was_disabled && !other_ssid->disabled)
wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
}
if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/* We are already associated with the selected network */
wpa_printf(MSG_DEBUG, "Already associated with the "
"selected network - do nothing");
return;
}
if (ssid) {
wpa_s->current_ssid = ssid;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->connect_without_scan =
(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
/*
* Don't optimize next scan freqs since a new ESS has been
* selected.
*/
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
} else {
wpa_s->connect_without_scan = NULL;
}
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s_clear_sae_rejected(wpa_s);
wpa_s->last_owe_group = 0;
if (ssid) {
ssid->owe_transition_bss_select_count = 0;
wpa_s_setup_sae_pt(wpa_s->conf, ssid);
}
if (wpa_s->connect_without_scan ||
wpa_supplicant_fast_associate(wpa_s) != 1) {
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpas_scan_reset_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
}
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
}
external/wpa_supplicant_8/wpa_supplicant/events.c
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_NO_SCAN_PROCESSING
return -1;
#else /* CONFIG_NO_SCAN_PROCESSING */
struct os_reltime now;
wpa_s->ignore_post_flush_scan_res = 0;
if (wpa_s->last_scan_res_used == 0)
return -1;
os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->last_scan,
SCAN_RES_VALID_FOR_CONNECT)) {
wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
return -1;
}
return wpas_select_network_from_last_scan(wpa_s, 0, 1);// select and connect
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
int time_to_reenable = wpas_reenabled_network_time(wpa_s);
if (time_to_reenable > 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Postpone network selection by %d seconds since all networks are disabled",
time_to_reenable);
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
eloop_register_timeout(time_to_reenable, 0,
wpas_network_reenabled, wpa_s, NULL);
return 0;
}
if (wpa_s->p2p_mgmt)
return 0; /* no normal connection on p2p_mgmt interface */
wpa_s->owe_transition_search = 0;
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
wpa_msg(wpa_s, MSG_INFO,
"Avoiding join because we already joined a mesh group");
return 0;
}
#endif /* CONFIG_MESH */
if (selected) {
int skip;
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
if (skip) {
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
return 0;
}
wpa_s->suitable_network++;
if (ssid != wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
* Do not allow other virtual radios to trigger operations based
* on these scan results since we do not want them to start
* other associations at the same time.
*/
return 1;
} else {
wpa_s->no_suitable_network++;
wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
ssid = wpa_supplicant_pick_new_network(wpa_s);
if (ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
wpa_supplicant_associate(wpa_s, NULL, ssid);
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else if (own_request) {
/*
* No SSID found. If SCAN results are as a result of
* own scan request and not due to a scan request on
* another shared interface, try another scan.
*/
int timeout_sec = wpa_s->scan_interval;
int timeout_usec = 0;
#ifdef CONFIG_P2P
int res;
res = wpas_p2p_scan_no_go_seen(wpa_s);
if (res == 2)
return 2;
if (res == 1)
return 0;
if (wpa_s->p2p_in_provisioning ||
wpa_s->show_group_started ||
wpa_s->p2p_in_invitation) {
/*
* Use shorter wait during P2P Provisioning
* state and during P2P join-a-group operation
* to speed up group formation.
*/
timeout_sec = 0;
timeout_usec = 250000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->auto_interworking &&
wpa_s->conf->interworking &&
wpa_s->conf->cred) {
wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
"start ANQP fetch since no matching "
"networks found");
wpa_s->network_select = 1;
wpa_s->auto_network_select = 1;
interworking_start_fetch_anqp(wpa_s);
return 1;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_WPS
if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
timeout_sec = 0;
timeout_usec = 500000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_OWE
if (wpa_s->owe_transition_search) {
wpa_dbg(wpa_s, MSG_DEBUG,
"OWE: Use shorter wait during transition mode search");
timeout_sec = 0;
timeout_usec = 500000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_OWE */
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_NETWORK_NOT_FOUND);
}
}
return 0;
}
按照流程图,我们会发现 wpas_select_network_from_last_scan
中有两个比较重要的调用,一个是 wpa_supplicant_pick_network
该函数的作用是从wpa_ssid中选择wpa_bss;一个是 wpa_supplicant_connect
进行关联和握手。我们先看wpa_supplicant_pick_network
,代码位置还是在 external/wpa_supplicant_8/wpa_supplicant/events.c
struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid **selected_ssid)
{
struct wpa_bss *selected = NULL;
size_t prio;
struct wpa_ssid *next_ssid = NULL;
struct wpa_ssid *ssid;
if (wpa_s->last_scan_res == NULL ||
wpa_s->last_scan_res_used == 0)
return NULL; /* no scan results from last update */
if (wpa_s->next_ssid) {
/* check that next_ssid is still valid */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->next_ssid)
break;
}
next_ssid = ssid;
wpa_s->next_ssid = NULL;
}
while (selected == NULL) {
//conf是struct wpa_supplicant中表示wpa_supplicant.conf文件的struct wpa_config
//num_prio表示wpa_config中wpa_ssid中的优先级有几种类型,有可能一个优先级下面挂有多个wpa_ssid
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
//pssid表示wpa_config中按prio排列的wpa_ssid链表的头节点
if (next_ssid && next_ssid->priority ==
wpa_s->conf->pssid[prio]->priority) {
//only_first_ssid = 1表示从last selected的优先级最高的排在优先级链表第一位的ssid中选择bss
selected = wpa_supplicant_select_bss(
wpa_s, next_ssid, selected_ssid, 1);
if (selected)
break;
}
//only_first_ssid = 0表示从整个优先级链表中选择bss
selected = wpa_supplicant_select_bss(
wpa_s, wpa_s->conf->pssid[prio],
selected_ssid, 0);
if (selected)
break;
}
if (selected == NULL && wpa_s->blacklist &&
!wpa_s->countermeasures) {
wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
"blacklist and try again");
wpa_blacklist_clear(wpa_s);
wpa_s->blacklist_cleared++;
} else if (selected == NULL)
break;
}
ssid = *selected_ssid;
if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
!ssid->passphrase && !ssid->ext_psk) {
const char *field_name, *txt = NULL;
wpa_dbg(wpa_s, MSG_DEBUG,
"PSK/passphrase not yet available for the selected network");
wpas_notify_network_request(wpa_s, ssid,
WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
field_name = wpa_supplicant_ctrl_req_to_string(
WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
if (field_name == NULL)
return NULL;
wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
selected = NULL;
}
return selected;
}
wpa_supplicant_select_bss
具体处理如何从priority group中选择bss
static struct wpa_bss *
wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
struct wpa_ssid *group,
struct wpa_ssid **selected_ssid,
int only_first_ssid)
{
unsigned int i;
if (wpa_s->current_ssid) {
struct wpa_ssid *ssid;
wpa_dbg(wpa_s, MSG_DEBUG,
"Scan results matching the currently selected network");
//last_scan_res_used表示上次扫描的bss的个数
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
// wpa_scan_res_match用来匹配SSID,加密方式等
ssid = wpa_scan_res_match(wpa_s, i, bss, group,
only_first_ssid, 0);
if (ssid != wpa_s->current_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR
" freq=%d level=%d snr=%d est_throughput=%u",
i, MAC2STR(bss->bssid), bss->freq, bss->level,
bss->snr, bss->est_throughput);
}
}
if (only_first_ssid)
wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
group->id);
else
wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
group->priority);
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
wpa_s->owe_transition_select = 1;
//selected_ssid不等于NULL表示调用成功,真正的返回值是wpa_bss
*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
only_first_ssid, 1);
wpa_s->owe_transition_select = 0;
if (!*selected_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, " selected %sBSS " MACSTR
" ssid='%s'",
bss == wpa_s->current_bss ? "current ": "",
MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len));
return bss;
}
return NULL;
}
接着我们回到 wpas_select_network_from_last_scan
看下 wpa_supplicant_connect
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
wpas_notify_wps_event_pbc_overlap(wpa_s);
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
wpa_s->p2p_in_provisioning) {
eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
wpa_s, NULL);
return -1;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
wpas_wps_pbc_overlap(wpa_s);
wpas_wps_cancel(wpa_s);
#endif /* CONFIG_WPS */
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG,
"Considering connect request: reassociate: %d selected: "
MACSTR " bssid: " MACSTR " pending: " MACSTR
" wpa_state: %s ssid=%p current_ssid=%p",
wpa_s->reassociate, MAC2STR(selected->bssid),
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
wpa_supplicant_state_txt(wpa_s->wpa_state),
ssid, wpa_s->current_ssid);
/*
* Do not trigger new association unless the BSSID has changed or if
* reassociation is requested. If we are in process of associating with
* the selected BSSID, do not trigger new attempt.
*/
if (wpa_s->reassociate ||
(os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
((wpa_s->wpa_state != WPA_ASSOCIATING &&
wpa_s->wpa_state != WPA_AUTHENTICATING) ||
(!is_zero_ether_addr(wpa_s->pending_bssid) &&
os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
0) ||
(is_zero_ether_addr(wpa_s->pending_bssid) &&
ssid != wpa_s->current_ssid)))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
return 0;
}
wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
MAC2STR(selected->bssid));
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
"connect with the selected AP");
}
return 0;
}
我们可以看到 wpa_supplicant_connect
会调用 wpa_supplicant.c
的 wpa_supplicant_associate
进行关联,关联成功就是四次握手。