首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android11 WiFi连接流程梳理

Android11 WiFi连接流程梳理

作者头像
用户7557625
发布2021-09-14 14:57:49
3.1K0
发布2021-09-14 14:57:49
举报

梳理一下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

总体流程如下图,第一次画流程图,有点丑。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档