当前位置:首页 » 《关注互联网》 » 正文

Android11 WiFi连接流程梳理_一个Android菜鸟的博客

39 人参与  2021年09月12日 16:03  分类 : 《关注互联网》  评论

点击全文阅读


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

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

请添加图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/27663.html

网络  连接  流程  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 网站收录 | 免费二级域名 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备12007198号-1