本文介绍: Android S 双STA

因为特性研发需求回归framework层看些相关的东西(组里能做fwk开发的少之又少)
以下是对双STA android fwk实现的一些研读(高效起见,不拿Andoird R的源码做对比了(双STA是Android S新增特性))

写这篇文章参考 Wi-Fi STA/STA并发

双STA区分双连接,首先是针对原来单一的 ClientModeManager 补充了
Primary + Second设计

一些配置相关
//ActiveModeWarden.java
    /**
     * @return Returns whether the device can support at least two concurrent client mode managers
     * and the local only use-case is enabled.
     */
    public boolean isStaStaConcurrencySupportedForLocalOnlyConnections() {
        return mWifiNative.isStaStaConcurrencySupported()
                && mContext.getResources().getBoolean(
                        R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled);
    }

    /**
     * @return Returns whether the device can support at least two concurrent client mode managers
     * and the mbb wifi switching is enabled.
     */
    public boolean isStaStaConcurrencySupportedForMbb() {
        return mWifiNative.isStaStaConcurrencySupported()
                && mContext.getResources().getBoolean(
                        R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled);
    }

    /**
     * @return Returns whether the device can support at least two concurrent client mode managers
     * and the restricted use-case is enabled.
     */
    public boolean isStaStaConcurrencySupportedForRestrictedConnections() {
        return mWifiNative.isStaStaConcurrencySupported()
                && mContext.getResources().getBoolean(
                        R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled);
    }

WifiNative是按 driver capa来判断是否支持的;另外有三种开关config.xml
以MTK机器为例
/vendor/mediatek/proprietary/packages/overlay/vendor/WifiResOverlay/concurrency/dual_sta/res/values/config.xml
/packages/modules/Wifi/service/ServiceWifiResources/res/values/config.xml

mWifiNative.isStaStaConcurrencySupported() ->> 取决于 HalDeviceManager对接口combo的配置(要允许 [STA, 2])

//WifiVendorHal.java
	/**
	 * Returns whether STA + STA concurrency is supported or not.
	 */
	public boolean isStaStaConcurrencySupported() {
	    synchronized (sLock) {
	        return mHalDeviceManager.canSupportIfaceCombo(new SparseArray<Integer>() {{
	                put(IfaceType.STA, 2);
	            }});
	    }
	}
触发连接
//WifiServiceImpl.java
public void connect(WifiConfiguration config, int netId, @Nullable IActionListener callback){
	mMakeBeforeBreakManager.stopAllSecondaryTransientClientModeManagers(() ->
                    mConnectHelper.connectToNetwork(result, wrapper, uid));
}

//ConnectHelper.java
public void connectToNetwork(
            @NonNull ClientModeManager clientModeManager,
            @NonNull NetworkUpdateResult result,
            @NonNull ActionListenerWrapper wrapper,
            int callingUid) {
	clientModeManager.connectNetwork(result, wrapper, callingUid);
}

连接动作由”指派”的 clientModeManager 进行

关注
mMakeBeforeBreakManager.stopAllSecondaryTransientClientModeManagers(() -> mConnectHelper.connectToNetwork(result, wrapper, uid));

ActiveModeWarden维护的只有一个 PrimaryClientModeManager 但可能会有很多 SecondaryClientModeManager
SecondaryClientModeManager 有机会转变成 PrimaryClientModeManager (ClientRole的转变)

这里摆一下下ClientRole的继承关系
在这里插入图片描述

SecondaryTransient 角色的ClientModeManager将很快转换自己角色,为了避免混乱,MBB要求所有的 transient 都先停下来,全部停下来只有再进行连接动作。源码这里调用的是不带 ClienModeManager 的写法,即让默认的 PrimaryClientModeManager 去触发连接。实际这里需要做些修改。(09-14回顾,这里这样写是没有问题的,这种WifiManager定死ConnectHelper用PrimaryClientModeManager的写法意味着,双STA默认不允许用户wifi界面去自主选择第二个网络连接第二个网络的选择连接应该交由framework完成,用户能做的只是决定 这个特性 的开关)

这里需要先看下ClientModeManager的创建情况。

//ActiveModeWarden.java
//wifi开关打开的,则Primary,否则看 Location + WiFi Scanning -> ScanOnly
    private ActiveModeManager.ClientRole getRoleForPrimaryOrScanOnlyClientModeManager() {
        if (mSettingsStore.isWifiToggleEnabled()) {
            return ROLE_CLIENT_PRIMARY;
        } else if (mWifiController.shouldEnableScanOnlyMode()) {
            return ROLE_CLIENT_SCAN_ONLY;
        } else {
            Log.e(TAG, "Something is wrong, no client mode toggles enabled");
            return null;
        }
    }

class WifiController {
        @Override
        public void start() {
            ActiveModeManager.ClientRole role = getRoleForPrimaryOrScanOnlyClientModeManager();
            if (role == ROLE_CLIENT_PRIMARY) {
                startPrimaryClientModeManager(mLastPrimaryClientModeManagerRequestorWs);
                setInitialState(mEnabledState);
            } else if (role == ROLE_CLIENT_SCAN_ONLY) {
                startScanOnlyClientModeManager(mLastScanOnlyClientModeManagerRequestorWs);
                setInitialState(mEnabledState);
            } else {
                setInitialState(mDisabledState);
            }
            
            // Initialize the lower layers before we start.
            mWifiNative.initialize();
            super.start();
        }
}

初始设计为:
只有wifi打开时会创建PrimaryClientModeManager
什么时候创建第二个,且不同ClientRole 的 ClientModeManager 呢?

关注ActiveModeWarden中

private final Set<ConcreteClientModeManager> mClientModeManagers = new ArraySet<>();

这个Set的Add和Remove动作,溯源即得到以下流程逻辑

  1. WifiConnectivityManager.handleScanResults
private void handleScanResults(@NonNull List<ScanDetail> scanDetails,
		@NonNull String listenerName,
		boolean isFullScan,
		@NonNull HandleScanResultsListener handleScanResultsListener) {

	boolean hasExistingSecondaryCmm = false;
	for (ClientModeManager clientModeManager :
			mActiveModeWarden.getInternetConnectivityClientModeManagers()) {
		if (clientModeManager.getRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED) {
			hasExistingSecondaryCmm = true;
		}
	}

	// We don't have any existing secondary CMM, but are we allowed to create a secondary CMM
	// and do we have a request for OEM_PAID/OEM_PRIVATE request? If yes, we need to perform
	// network selection to check if we have any potential candidate for the secondary CMM
	// creation.
	if (!hasExistingSecondaryCmm
			&& (mOemPaidConnectionAllowed || mOemPrivateConnectionAllowed)) {
		// prefer OEM PAID requestor if it exists.
		WorkSource oemPaidOrOemPrivateRequestorWs =
				mOemPaidConnectionRequestorWs != null
						? mOemPaidConnectionRequestorWs
						: mOemPrivateConnectionRequestorWs;
		if (oemPaidOrOemPrivateRequestorWs != null
				&& mActiveModeWarden.canRequestMoreClientModeManagersInRole(
						oemPaidOrOemPrivateRequestorWs,
						ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
			// Add a placeholder CMM state to ensure network selection is performed for a
			// potential second STA creation.
			cmmStates.add(new WifiNetworkSelector.ClientModeManagerState());
		}
	}

	// Check if any blocklisted BSSIDs can be freed.
	//...

	List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan(
			scanDetails, bssidBlocklist, cmmStates, mUntrustedConnectionAllowed,
			mOemPaidConnectionAllowed, mOemPrivateConnectionAllowed);

	// We have an oem paid/private network request and device supports STA + STA, check if there
	// are oem paid/private suggestions.
	if ((mOemPaidConnectionAllowed || mOemPrivateConnectionAllowed)
			&& mActiveModeWarden.isStaStaConcurrencySupportedForRestrictedConnections()) {
		// Split the candidates based on whether they are oem paid/oem private or not.
		Map<Boolean, List<WifiCandidates.Candidate>> candidatesPartitioned =
				candidates.stream()
						.collect(Collectors.groupingBy(c -> c.isOemPaid() || c.isOemPrivate()));
		List<WifiCandidates.Candidate> primaryCmmCandidates =
				candidatesPartitioned.getOrDefault(false, Collections.emptyList());
		List<WifiCandidates.Candidate> secondaryCmmCandidates =
				candidatesPartitioned.getOrDefault(true, Collections.emptyList());
		// Some oem paid/private suggestions found, use secondary cmm flow.
		if (!secondaryCmmCandidates.isEmpty()) {
			handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable(
					listenerName, primaryCmmCandidates, secondaryCmmCandidates,
					handleScanResultsListener);
			return;
		}
		// intentional fallthrough: No oem paid/private suggestions, fallback to legacy flow.
	}
	handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
			listenerName, candidates, handleScanResultsListener);
}

总结:在对scan results处理中,如果扫描到的网络中有 OEM_PAID 或 OEM_PRIVATE 类型的网络,则按 非oem_paid/oem_private 和 oem_paid/oem_private 将候选网络分成两类,前者塞到 primaryCmmCandidates 中去,后者塞到 secondaryCmmCandidates 中去
之后调用 handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable 方法对 oem_paid/oem_private网络 进行处理,看有无必要创建一个 CMM

  1. 对 secondaryCmmCandidates 进行WNS选网操作,如果有合适的网络,就 requestSecondaryLongLivedClientModeManager ,即创建一个ClientRole == SECONDARY_LONG_LIVED 的CMM,并 connectToNetworkUsingCmmWithoutMbb 触发连接
private void handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable(
		@NonNull String listenerName,
		@NonNull List<WifiCandidates.Candidate> primaryCmmCandidates,
		@NonNull List<WifiCandidates.Candidate> secondaryCmmCandidates,
		@NonNull HandleScanResultsListener handleScanResultsListener) {
	// Perform network selection among secondary candidates.
	WifiConfiguration secondaryCmmCandidate =
			mNetworkSelector.selectNetwork(secondaryCmmCandidates);
	// No oem paid/private selected, fallback to legacy flow (should never happen!).
	if (secondaryCmmCandidate == null
			|| secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate() == null
			|| (!secondaryCmmCandidate.oemPaid && !secondaryCmmCandidate.oemPrivate)) {
		handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
				listenerName,
				Stream.concat(primaryCmmCandidates.stream(), secondaryCmmCandidates.stream())
						.collect(Collectors.toList()),
				handleScanResultsListener);
		return;
	}
	String secondaryCmmCandidateBssid =
			secondaryCmmCandidate.getNetworkSelectionStatus().getCandidate().BSSID;

	// At this point secondaryCmmCandidate must be either oemPaid, oemPrivate, or both.
	// OEM_PAID takes precedence over OEM_PRIVATE, so attribute to OEM_PAID requesting app.
	WorkSource secondaryRequestorWs = secondaryCmmCandidate.oemPaid
			? mOemPaidConnectionRequestorWs : mOemPrivateConnectionRequestorWs;

	WifiConfiguration primaryCmmCandidate =
			mNetworkSelector.selectNetwork(primaryCmmCandidates);
	// Request for a new client mode manager to spin up concurrent connection
	mActiveModeWarden.requestSecondaryLongLivedClientModeManager(
			(cm) -> {
				// Don't use make before break for these connection requests.

				// If we also selected a primary candidate trigger connection.
				if (primaryCmmCandidate != null) {
					localLog(listenerName + ":  WNS candidate(primary)-"
							+ primaryCmmCandidate.SSID);
					connectToNetworkUsingCmmWithoutMbb(
							getPrimaryClientModeManager(), primaryCmmCandidate);
				}

				localLog(listenerName + ":  WNS candidate(secondary)-"
						+ secondaryCmmCandidate.SSID);
				// Secndary candidate cannot be null (otherwise we would have switched to legacy flow above)
				connectToNetworkUsingCmmWithoutMbb(cm, secondaryCmmCandidate);

				handleScanResultsWithCandidate(handleScanResultsListener);
			}, secondaryRequestorWs,
			secondaryCmmCandidate.SSID,
			mConnectivityHelper.isFirmwareRoamingSupported()
					? null : secondaryCmmCandidateBssid);
}
  1. 如果候选网络中没有上述的 oem_paid/oem_private 类型的网络或者经过选网没有合适的网络可以加入,那么就走legacy flow,即 handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable
private void handleCandidatesFromScanResultsForPrimaryCmmUsingMbbIfAvailable(
		@NonNull String listenerName, @NonNull List<WifiCandidates.Candidate> candidates,
		@NonNull HandleScanResultsListener handleScanResultsListener) {
	WifiConfiguration candidate = mNetworkSelector.selectNetwork(candidates);
	if (candidate != null) {
		localLog(listenerName + ":  WNS candidate-" + candidate.SSID);
		connectToNetworkForPrimaryCmmUsingMbbIfAvailable(candidate);
		handleScanResultsWithCandidate(handleScanResultsListener);
	} else {
		localLog(listenerName + ":  No candidate");
		handleScanResultsWithNoCandidate(handleScanResultsListener);
	}
}

重点在于 connectToNetworkForPrimaryCmmUsingMbbIfAvailable

/**
 * Trigger network connection for primary client mode manager using make before break.
 *
 * Note: This may trigger make before break on a secondary STA if available which will
 * eventually become primary after validation or torn down if it does not become primary.
 */
 private void connectToNetworkForPrimaryCmmUsingMbbIfAvailable(
		@NonNull WifiConfiguration candidate) {
	ClientModeManager primaryManager = mActiveModeWarden.getPrimaryClientModeManager();
	connectToNetworkUsingCmm(
			primaryManager, candidate,
			new ConnectHandler() {
				@Override
				public void triggerConnectWhenDisconnected(
						WifiConfiguration targetNetwork,
						String targetBssid) {
					triggerConnectToNetworkUsingCmm(primaryManager, targetNetwork, targetBssid);
					// since using primary manager to connect, stop any existing managers in the
					// secondary transient role since they are no longer needed.
					mActiveModeWarden.stopAllClientModeManagersInRole(
							ROLE_CLIENT_SECONDARY_TRANSIENT);
				}

				@Override
				public void triggerConnectWhenConnected(
						WifiConfiguration currentNetwork,
						WifiConfiguration targetNetwork,
						String targetBssid) {
					// If both the current & target networks have MAC randomization disabled,
					// we cannot use MBB because then both ifaces would need to use the exact
					// same MAC address (the "designated" factory MAC for the device), which is
					// illegal. Fallback to single STA behavior.
					if (currentNetwork.macRandomizationSetting == RANDOMIZATION_NONE
							&& targetNetwork.macRandomizationSetting == RANDOMIZATION_NONE) {
						triggerConnectToNetworkUsingCmm(
								primaryManager, targetNetwork, targetBssid);
						// since using primary manager to connect, stop any existing managers in
						// the secondary transient role since they are no longer needed.
						mActiveModeWarden.stopAllClientModeManagersInRole(
								ROLE_CLIENT_SECONDARY_TRANSIENT);
						return;
					}
					// Else, use MBB if available.
					triggerConnectToNetworkUsingMbbIfAvailable(targetNetwork, targetBssid);
				}

				@Override
				public void triggerRoamWhenConnected(
						WifiConfiguration currentNetwork,
						WifiConfiguration targetNetwork,
						String targetBssid) {
					triggerRoamToNetworkUsingCmm(
							primaryManager, targetNetwork, targetBssid);
					// since using primary manager to connect, stop any existing managers in the
					// secondary transient role since they are no longer needed.
					mActiveModeWarden.stopAllClientModeManagersInRole(
							ROLE_CLIENT_SECONDARY_TRANSIENT);
				}
			});
}

对于双STA,我们关注 triggerConnectWhenConnected 情景下 ,当前网络和目标网络都支持 MAC randomization ,即调用triggerConnectToNetworkUsingMbbIfAvailable 方法
此方法核心在于创建一个 ClientRole == SECONDARY_TRANSIENT 的CMM,并用得到的CMM去triggerConnectToNetworkUsingCmm

/**
 * Trigger connection to a new wifi network while being connected to another network.
 * Depending on device configuration, this uses
 *  - MBB make before break (Dual STA), or
 *  - BBM break before make (Single STA)
 */
private void triggerConnectToNetworkUsingMbbIfAvailable(
		@NonNull WifiConfiguration targetNetwork, @NonNull String targetBssid) {
	// Request a ClientModeManager from ActiveModeWarden to connect with - may be an existing
	// CMM or a newly created one (potentially switching networks using Make-Before-Break)
	mActiveModeWarden.requestSecondaryTransientClientModeManager(
			(@Nullable ClientModeManager clientModeManager) -> {
				localLog("connectToNetwork: received requested ClientModeManager "
						+ clientModeManager);
				// we don't know which ClientModeManager will be allocated to us. Thus, double
				// check if we're already connected before connecting.
				if (isClientModeManagerConnectedOrConnectingToCandidate(
						clientModeManager, targetNetwork)) {
					localLog("connectToNetwork: already connected or connecting to candidate="
							+ targetNetwork + " on " + clientModeManager);
					return;
				}
				triggerConnectToNetworkUsingCmm(clientModeManager, targetNetwork, targetBssid);
			},
			ActiveModeWarden.INTERNAL_REQUESTOR_WS,
			targetNetwork.SSID,
			mConnectivityHelper.isFirmwareRoamingSupported() ? null : targetBssid);
}

09-29 补充:完善MBB机制

上述基本没有涉及MBB机制
这里直接放下我实测的MBB流程

  1. 创建一个 ClientRole == SECONDARY_TRANSIENT 的CMM,并用得到的CMM去triggerConnectToNetworkUsingCmm
    这时观察wpa_supplicantlog可以发现有创建wlan1接口以及connect动作,直到 CTRL-EVENT-CONNECTED 事件上报

  2. SecondaryTransientCMM 注册 WifiNetworkAgent 到 CnnectivityService,probe http/https通过
    MakeBeforeBreakManager中ClientModeImplMonitor注册监听器回调 onInternetValidated
    具体操作有: 将原PrimaryCMM的角色设置成 ROLE_CLIENT_SECONDARY_TRANSIENT

  3. 有CMM角色发生改变,即触发MakeBeforeBreakManager 注册回调onActiveModeManagerRoleChanged
    执行 recoveryPrimary以及maybeContinueMakeBeforeBreak方法
    具体操作有: 将 SecondaryTransientCMM 的角色设置成 PRIMARY, 并将原PrimaryCMM(新的SecondaryTransientCMM )减掉一些网络评分

  4. 由于新的SecondaryTransientCMM在ConnectivityService中的分低触发linger,timer定时(30s)到了之后会被teardown
    注:这方面源码不大确定,读者可自行查阅ConnectivityService handleLingerComplete(teardownUnneededNetwork)方法

显然,对于双STA来说,希望在MBB的基础上保持被从主wifi位置上拉下来的辅wifi连接,所以方法目前我只想到两种

  1. 注释减分动作,实测可行
  2. 无限期延长linger时长(linger时长默认先从系统属性读取没有就取30s,试过设置这个系统属性无效,也许是我手法问题)

这里,双STA在fwk设计基本就介绍完了,简单来说,要在MBB的基础上做一些魔改

原文地址:https://blog.csdn.net/AngryDog1024/article/details/126780101

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_37182.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注