初探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_supplicant的log可以发现有创建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的基础上做一些魔改

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
As an AI language model, I can provide an overview of the basics of STA (Static Timing Analysis). Static Timing Analysis (STA) is a method used to verify the timing performance of an integrated circuit (IC) design. It is a critical step in the design and manufacturing process of semiconductor devices. STA helps to ensure that the design meets the timing requirements and operates correctly under various conditions. The STA process involves analyzing the delays of a circuit design, including the delays of all the paths, from inputs to outputs. Timing analysis tools are used to determine the worst-case delay and to ensure that the circuit meets the required timing constraints. Timing constraints are typically specified in the form of timing paths, which define the maximum delay allowed for each path in the circuit. STA is performed at various stages of the design process, from the initial design to the final implementation. In the early stages of the design process, STA is used to estimate the performance of the design and to identify potential timing issues. As the design progresses, STA is used to verify the timing performance of the implemented design. STA involves several key steps, including: 1. Timing modeling: The timing behavior of each component in the circuit is modeled using delay models, which describe the delay of the component as a function of its input and output signals. 2. Timing analysis: The timing analysis tool analyzes the circuit to determine the worst-case delay and to identify any timing violations. 3. Timing optimization: Timing optimization techniques are used to improve the timing performance of the design, such as changing the placement of components or adjusting the clock frequency. 4. Timing verification: The final step is to verify that the design meets the timing requirements, including the timing paths and the overall timing budget. In conclusion, STA is a critical step in semiconductor design and manufacturing. It helps to ensure that the design meets the timing requirements and operates correctly under various conditions. STA involves timing modeling, analysis, optimization, and verification to ensure that the design meets the required timing constraints.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值