STA双WiFi连接
- 1、STA/STA双WiFi开关
- 1.1 相关属性
- 1.2 STA/STA支持判断
- 2、STA双WiFi命令测试
- 2.1 adb shell cmd wifi add-suggestion guest_5G wpa3 12345678 -p
- 2.2 adb shell cmd wifi remove-suggestion guest_5G
- 2.3 查看dumpsys wifi信息WifiConfigStore
- 3、STA双WiFi连接流程
- 3.1 STA双WiFi请求前准备工作
- 3.2 STA双WiFi连接时序图
Wi-Fi STA/STA 并发:Android 12 引入了 Wi-Fi STA/STA 并发功能,使设备可同时连接到两个 Wi-Fi 网络
1、STA/STA双WiFi开关
1.1 相关属性
packages/modules/Wifi/service/ServiceWifiResources/res/values/config.xml
<!-- Enable Make-Before-Break Wifi network switching.
Note: this is conditional on the device supporting dual concurrent STAs. -->
<bool translatable="false" name="config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled">false</bool>
<!-- Enable concurrent peer to peer + internet connectivity
Note: this is conditional on the device supporting dual concurrent STAs. -->
<bool translatable="false" name="config_wifiMultiStaLocalOnlyConcurrencyEnabled">false</bool>
<!-- Enable concurrent restricted connectivity + internet connectivity
Note: this is conditional on the device supporting dual concurrent STAs. -->
<bool translatable="false" name="config_wifiMultiStaRestrictedConcurrencyEnabled">false</bool>
<!-- Enable concurrent internet connectivity + internet connectivity
Note: this is conditional on the device supporting dual concurrent STAs. -->
<bool translatable="false" name="config_wifiMultiStaMultiInternetConcurrencyEnabled">false</bool>
1.2 STA/STA支持判断
mWifiManager.isWifiEnabled()
WIFI开关是否打开mWifiManager.isStaConcurrencyForMultiInternetSupported()
属性config_wifiMultiStaMultiInternetConcurrencyEnabled
配置mWifiManager.getStaConcurrencyForMultiInternetMode() == WifiManager.WIFI_MULTI_INTERNET_MODE_MULTI_AP
模式双WIFI STA/STA开关是否打开(模式设置setStaConcurrencyForMultiInternetMode
)
WifiManager#WIFI_MULTI_INTERNET_MODE_DBS_AP
:限制与 DBS AP 的双频的并发连接。WifiManager#WIFI_MULTI_INTERNET_MODE_MULTI_AP
:连接到任意 AP,其中的各个连接使用不同的频段。WifiManager#WIFI_MULTI_INTERNET_MODE_DISABLED
:停用功能。
2、STA双WiFi命令测试
adb shell cmd wifi
(Android framework服务命令行工具框架 - Android13)
packages/modules/Wifi/service/java/com/android/server/wifi/WifiShellCommand.java
- 启用详细日志记录:
adb shell cmd wifi set-verbose-logging enabled
- 清理旧的wifi建议(如果有的话):
adb shell cmd wifi remove-all-suggestions
adb shell cmd wifi list-all-suggestions
- WiFi连接状态查询:
adb shell cmd wifi status | grep -E "==|connected to"
==== ClientModeManager instance: ConcreteClientModeManager{id=33409 iface=wlan0 role=ROLE_CLIENT_PRIMARY} ==== Wifi is connected to "guest_5G" ==== ClientModeManager instance: ConcreteClientModeManager{id=82271673 iface=wlan1 role=ROLE_CLIENT_SECONDARY_LONG_LIVED} ==== Wifi is connected to "guest"
- 添加WifiNetworkSuggestion网络连接:
adb shell cmd wifi add-suggestion guest_5G wpa3 12345678 -p
- 执行网络扫描:
adb shell cmd wifi start-scan
- 断开移除WifiNetworkSuggestion网络:
adb shell cmd wifi remove-suggestion guest_5G
2.1 adb shell cmd wifi add-suggestion guest_5G wpa3 12345678 -p
添加WifiNetworkSuggestion网络连接:
- 参数
guest_5G wpa3 12345678
网络名称、类型、密码- 参数
-p
(或-o
)设置setOemPaid(true)
、setOemPrivate(true)
(设备能够连接到副Wi-Fi网络:添加 Wi-Fi 网络建议,方法是将 setOemPaid 或 setOemPrivate 设置为 true。)mConnectivityManager.requestNetwork(networkRequest, networkCallback)
:TRANSPORT_WIFI
网络请求
(在 ConnectivityManager 中,提交具有相应功能的 NetworkRequest:为 setOemPaid 提交 NET_CAPABILITY_OEM_PAID 功能(为 setOemPrivate 提交 NET_CAPABILITY_OEM_PRIVATE 功能))
case "add-suggestion": {
WifiNetworkSuggestion suggestion = buildSuggestion(pw);
if (suggestion == null) {
pw.println("Invalid network suggestion parameter");
return -1;
}
int errorCode = mWifiService.addNetworkSuggestions(
Arrays.asList(suggestion), SHELL_PACKAGE_NAME, null);
if (errorCode != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
pw.println("Add network suggestion failed with error code: " + errorCode);
return -1;
}
// untrusted/oem-paid networks need a corresponding NetworkRequest.
if (suggestion.isUntrusted()
|| (SdkLevel.isAtLeastS()
&& (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
NetworkRequest.Builder networkRequestBuilder =
new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI);
if (suggestion.isUntrusted()) {
networkRequestBuilder.removeCapability(NET_CAPABILITY_TRUSTED);
}
if (SdkLevel.isAtLeastS()) {
if (suggestion.isOemPaid()) {
networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PAID);
}
if (suggestion.isOemPrivate()) {
networkRequestBuilder.addCapability(NET_CAPABILITY_OEM_PRIVATE);
}
}
NetworkRequest networkRequest = networkRequestBuilder.build();
ConnectivityManager.NetworkCallback networkCallback =
new ConnectivityManager.NetworkCallback();
pw.println("Adding request: " + networkRequest);
mConnectivityManager.requestNetwork(networkRequest, networkCallback);
sActiveRequests.put(
suggestion.getSsid(), Pair.create(networkRequest, networkCallback));
}
return 0;
}
//解析网络参数
private WifiNetworkSuggestion buildSuggestion(PrintWriter pw) {
String ssid = getNextArgRequired();
String type = getNextArgRequired();
WifiNetworkSuggestion.Builder suggestionBuilder =
new WifiNetworkSuggestion.Builder();
suggestionBuilder.setSsid(ssid);
if (TextUtils.equals(type, "wpa3")) {
suggestionBuilder.setWpa3Passphrase(getNextArgRequired());
} else if (TextUtils.equals(type, "wpa2")) {
suggestionBuilder.setWpa2Passphrase(getNextArgRequired());
} else if (TextUtils.equals(type, "owe")) {
suggestionBuilder.setIsEnhancedOpen(true);
} else if (TextUtils.equals(type, "open")) {
// nothing to do.
} else {
throw new IllegalArgumentException("Unknown network type " + type);
}
boolean isCarrierMerged = false;
String option = getNextOption();
while (option != null) {
if (option.equals("-u")) {
suggestionBuilder.setUntrusted(true);
} else if (option.equals("-o")) {
if (SdkLevel.isAtLeastS()) {
suggestionBuilder.setOemPaid(true);
} else {
throw new IllegalArgumentException(
"-o OEM paid suggestions not supported before S");
}
} else if (option.equals("-p")) {
if (SdkLevel.isAtLeastS()) {
suggestionBuilder.setOemPrivate(true);
} else {
throw new IllegalArgumentException(
"-p OEM private suggestions not supported before S");
}
} else if (option.equals("-m")) {
suggestionBuilder.setIsMetered(true);
} else if (option.equals("-s")) {
suggestionBuilder.setCredentialSharedWithUser(true);
} else if (option.equals("-d")) {
suggestionBuilder.setIsInitialAutojoinEnabled(false);
} else if (option.equals("-b")) {
suggestionBuilder.setBssid(MacAddress.fromString(getNextArgRequired()));
} else if (option.equals("-r")) {
if (SdkLevel.isAtLeastS()) {
suggestionBuilder.setMacRandomizationSetting(
WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT);
} else {
throw new IllegalArgumentException(
"-r non_persistent MAC randomization not supported before S");
}
} else if (option.equals("-a")) {
if (SdkLevel.isAtLeastS()) {
isCarrierMerged = true;
} else {
throw new IllegalArgumentException("-a option is not supported before S");
}
} else if (option.equals("-i")) {
if (SdkLevel.isAtLeastS()) {
int subId = Integer.parseInt(getNextArgRequired());
suggestionBuilder.setSubscriptionId(subId);
} else {
throw new IllegalArgumentException(
"-i subscription ID option is not supported before S");
}
} else if (option.equals("-c")) {
int carrierId = Integer.parseInt(getNextArgRequired());
suggestionBuilder.setCarrierId(carrierId);
} else if (option.equals("-h")) {
suggestionBuilder.setIsHiddenSsid(true);
} else {
pw.println("Ignoring unknown option " + option);
}
option = getNextOption();
}
WifiNetworkSuggestion suggestion = suggestionBuilder.build();
if (isCarrierMerged) {
if (suggestion.wifiConfiguration.subscriptionId
== SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
pw.println("Carrier merged network must have valid subscription Id");
return null;
}
suggestion.wifiConfiguration.carrierMerged = true;
}
return suggestion;
}
2.2 adb shell cmd wifi remove-suggestion guest_5G
断开移除WifiNetworkSuggestion网络:
mConnectivityManager.unregisterNetworkCallback(nrAndNc.second)
:移除请求时的NetworkCallback就可以断开WiFi网络mWifiService.removeNetworkSuggestions()
:移除WifiNetworkSuggestion网路信息,这里就会移除WifiConfigStoreNetworkSuggestions.xml
保存信息
case "remove-suggestion": {
String ssid = getNextArgRequired();
String action = getNextArg();
int actionCode = ACTION_REMOVE_SUGGESTION_DISCONNECT;
if (action != null && action.equals("lingering")) {
actionCode = ACTION_REMOVE_SUGGESTION_LINGER;
}
List<WifiNetworkSuggestion> suggestions =
mWifiService.getNetworkSuggestions(SHELL_PACKAGE_NAME);
WifiNetworkSuggestion suggestion = suggestions.stream()
.filter(s -> s.getSsid().equals(ssid))
.findAny()
.orElse(null);
if (suggestion == null) {
pw.println("No matching suggestion to remove");
return -1;
}
mWifiService.removeNetworkSuggestions(
Arrays.asList(suggestion), SHELL_PACKAGE_NAME, actionCode);
// untrusted/oem-paid networks need a corresponding NetworkRequest.
if (suggestion.isUntrusted()
|| (SdkLevel.isAtLeastS()
&& (suggestion.isOemPaid() || suggestion.isOemPrivate()))) {
Pair<NetworkRequest, ConnectivityManager.NetworkCallback> nrAndNc =
sActiveRequests.remove(suggestion.getSsid());
if (nrAndNc == null) {
pw.println("No matching request to remove");
return -1;
}
pw.println("Removing request: " + nrAndNc.first);
mConnectivityManager.unregisterNetworkCallback(nrAndNc.second);
}
return 0;
}
2.3 查看dumpsys wifi信息WifiConfigStore
packages/modules/Wifi/service/java/com/android/server/wifi/WifiConfigStore.java
packages/modules/Wifi/service/java/com/android/server/wifi/WifiConfigManager.java
packages/modules/Wifi/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java
Dump of WifiConfigStore
WifiConfigStore - Store File Begin ----
Name: /data/misc/apexdata/com.android.wifi/WifiConfigStore.xml, File Id: 0, Credentials encrypted: false
Name: /data/misc/apexdata/com.android.wifi/WifiConfigStoreSoftAp.xml, File Id: 1, Credentials encrypted: false
Name: /data/misc_ce/0/apexdata/com.android.wifi/WifiConfigStore.xml, File Id: 2, Credentials encrypted: false
Name: /data/misc_ce/0/apexdata/com.android.wifi/WifiConfigStoreNetworkSuggestions.xml, File Id: 3, Credentials encrypted: false
WifiConfigStore - Store Data Begin ----
3、STA双WiFi连接流程
3.1 STA双WiFi请求前准备工作
- 设置
setOemPaid
或setOemPrivate
,并mWifiService.addNetworkSuggestions
添加WifiNetworkSuggestion
信息NetworkRequest
请求信息添加TRANSPORT_WIFI
、NET_CAPABILITY_OEM_PRIVATE
(或NET_CAPABILITY_OEM_PAID
)mConnectivityManager.requestNetwork(networkRequest, networkCallback)
请网络,networkCallback
监听连接状态(断开是注销networkCallback
)- 请求执行到
OemWifiNetworkFactory.java#needNetworkFor
,OemPaidWifiNetworkFactory
有挂起的网络请求触发,设置mOemPaidConnectionAllowed、mOemPaidConnectionRequestorWs
,并开启自动联接setAutoJoinEnabled
和启动连接性扫描startConnectivityScan
packages/modules/Wifi/service/java/com/android/server/wifi/OemWifiNetworkFactory.java
protected void needNetworkFor(NetworkRequest networkRequest) {
if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) {
mOemPaidConnectionReqCount++;
if (mOemPaidConnectionReqCount == 1) {
mWifiConnectivityManager.setOemPaidConnectionAllowed(
true, new WorkSource(networkRequest.getRequestorUid(),
networkRequest.getRequestorPackageName()));
}
}
if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)) {
mOemPrivateConnectionReqCount++;
if (mOemPrivateConnectionReqCount == 1) {
mWifiConnectivityManager.setOemPrivateConnectionAllowed(
true, new WorkSource(networkRequest.getRequestorUid(),
networkRequest.getRequestorPackageName()));
}
}
}
packages/modules/Wifi/service/java/com/android/server/wifi/WifiConnectivityManager.java
public void setOemPaidConnectionAllowed(boolean allowed, WorkSource requestorWs) {
localLog("setOemPaidConnectionAllowed: allowed=" + allowed + ", requestorWs="
+ requestorWs);
if (mOemPaidConnectionAllowed != allowed) {
mOemPaidConnectionAllowed = allowed;
mOemPaidConnectionRequestorWs = requestorWs;
checkAllStatesAndEnableAutoJoin();
}
}
private void checkAllStatesAndEnableAutoJoin() {
// if auto-join was disabled externally, don't re-enable for any triggers.
// External triggers to disable always trumps any internal state.
setAutoJoinEnabled(mAutoJoinEnabledExternal
&& (mUntrustedConnectionAllowed || mOemPaidConnectionAllowed
|| mOemPrivateConnectionAllowed || mTrustedConnectionAllowed
|| mRestrictedConnectionAllowedUids.size() != 0 || hasMultiInternetConnection())
&& !mSpecificNetworkRequestInProgress);
startConnectivityScan(SCAN_IMMEDIATELY);
}
3.2 STA双WiFi连接时序图
接收到WifiScanner扫描结果
AllSingleScanListener#onResults
:
- 获取Wifi候选者
mNetworkSelector.getCandidatesFromScan()
- 当支持STA双WIFI连接且
secondaryCmmCandidates
不为空,执行handleCandidatesFromScanResultsUsingSecondaryCmmIfAvailable
去mActiveModeWarden.requestSecondaryLongLivedClientModeManager
请求创建副WiFi类型ROLE_CLIENT_SECONDARY_LONG_LIVED
的ConcreteClientModeManager
- 最后执行
connectToNetworkUsingCmmWithoutMbb > triggerConnectToNetworkUsingCmm > clientModeManager.startConnectToNetwork()
连接网络,只有就可主Wifi连接一样,只是不同的wlan0
或wlan1
。(Android WiFi 连接)