因为特性研发需求,回归framework层看些相关的东西(组里能做fwk开发的少之又少)
以下是对双STA android fwk实现的一些研读(高效起见,不拿Andoird R的源码做对比了(双STA是Android S新增特性))
双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的转变)
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动作,溯源即得到以下流程逻辑
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
- 对 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);
}
- 如果候选网络中没有上述的 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机制
-
创建一个 ClientRole == SECONDARY_TRANSIENT 的CMM,并用得到的CMM去triggerConnectToNetworkUsingCmm
这时观察wpa_supplicant的log可以发现有创建wlan1接口以及connect动作,直到 CTRL-EVENT-CONNECTED 事件上报 -
SecondaryTransientCMM 注册 WifiNetworkAgent 到 CnnectivityService,probe http/https,通过后
MakeBeforeBreakManager中ClientModeImplMonitor注册的监听器回调 onInternetValidated
具体操作有: 将原PrimaryCMM的角色设置成 ROLE_CLIENT_SECONDARY_TRANSIENT -
有CMM角色发生改变,即触发MakeBeforeBreakManager 注册的回调onActiveModeManagerRoleChanged
执行 recoveryPrimary以及maybeContinueMakeBeforeBreak方法
具体操作有: 将 SecondaryTransientCMM 的角色设置成 PRIMARY, 并将原PrimaryCMM(新的SecondaryTransientCMM )减掉一些网络评分 -
由于新的SecondaryTransientCMM在ConnectivityService中的分低,触发linger,timer定时(30s)到了之后会被teardown
注:这方面源码不大确定,读者可自行查阅ConnectivityService handleLingerComplete(teardownUnneededNetwork)方法
显然,对于双STA来说,希望在MBB的基础上保持被从主wifi位置上拉下来的辅wifi的连接,所以方法目前我只想到两种
到这里,双STA在fwk的设计基本就介绍完了,简单来说,要在MBB的基础上做一些魔改
原文地址:https://blog.csdn.net/AngryDog1024/article/details/126780101
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_37182.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!