diff --git a/README.md b/README.md
index 1aca42ab..f9ba8375 100644
--- a/README.md
+++ b/README.md
@@ -299,6 +299,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded
* (since API 28) `Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I,sdk,system-api,test-api`
* (since API 30) `Landroid/net/wifi/WifiManager;->getSoftApConfiguration()Landroid/net/wifi/SoftApConfiguration;,sdk,system-api,test-api`
* (prior to API 30) `Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,sdk,system-api,test-api`
+* (since API 30) `Landroid/net/wifi/WifiManager;->isApMacRandomizationSupported()Z,sdk,system-api,test-api`
* (since API 28) `Landroid/net/wifi/WifiManager;->registerSoftApCallback(Ljava/util/concurrent/Executor;Landroid/net/wifi/WifiManager$SoftApCallback;)V,sdk,system-api,test-api`
* (since API 30) `Landroid/net/wifi/WifiManager;->setSoftApConfiguration(Landroid/net/wifi/SoftApConfiguration;)Z,sdk,system-api,test-api`
* (prior to API 30) `Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,sdk,system-api,test-api`
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
index d7c8b491..3bb8b282 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt
@@ -28,10 +28,7 @@ import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.wifi.*
import be.mygod.vpnhotspot.root.WifiApCommands
-import be.mygod.vpnhotspot.util.format
-import be.mygod.vpnhotspot.util.joinToSpanned
-import be.mygod.vpnhotspot.util.makeMacSpan
-import be.mygod.vpnhotspot.util.readableMessage
+import be.mygod.vpnhotspot.util.*
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -203,7 +200,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
val capability = SoftApCapability(parcel)
val numClients = numClients
val maxClients = capability.maxSupportedClients
- var supportedFeatures = capability.supportedFeatures
+ var features = capability.supportedFeatures
if (BuildCompat.isAtLeastS()) for ((flag, band) in arrayOf(
SoftApCapability.SOFTAP_FEATURE_BAND_24G_SUPPORTED to SoftApConfigurationCompat.BAND_2GHZ,
SoftApCapability.SOFTAP_FEATURE_BAND_5G_SUPPORTED to SoftApConfigurationCompat.BAND_5GHZ,
@@ -212,17 +209,28 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
)) {
if (capability.getSupportedChannelList(band).isEmpty()) continue
// reduce double reporting
- supportedFeatures = supportedFeatures and flag.inv()
+ features = features and flag.inv()
}
val result = parent.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, numClients ?: 0)
.format(locale, numClients ?: "?", maxClients, sequence {
- var features = supportedFeatures
+ if (WifiApManager.isApMacRandomizationSupported) yield(parent.getText(
+ R.string.tethering_manage_wifi_feature_ap_mac_randomization))
+ if (Services.wifi.isStaApConcurrencySupported) yield(parent.getText(
+ R.string.tethering_manage_wifi_feature_sta_ap_concurrency))
+ if (BuildCompat.isAtLeastS()) {
+ if (Services.wifi.isBridgedApConcurrencySupported) yield(parent.getText(
+ R.string.tethering_manage_wifi_feature_bridged_ap_concurrency))
+ if (Services.wifi.isStaBridgedApConcurrencySupported) yield(parent.getText(
+ R.string.tethering_manage_wifi_feature_sta_bridged_ap_concurrency))
+ }
if (features != 0L) while (features != 0L) {
val bit = features.takeLowestOneBit()
yield(SoftApCapability.featureLookup(bit, true))
features = features and bit.inv()
- } else yield(parent.getText(R.string.tethering_manage_wifi_no_features))
- }.joinToSpanned())
+ }
+ }.joinToSpanned().let {
+ if (it.isEmpty()) parent.getText(R.string.tethering_manage_wifi_no_features) else it
+ })
if (BuildCompat.isAtLeastS()) {
val list = SoftApConfigurationCompat.BAND_TYPES.map { band ->
val channels = capability.getSupportedChannelList(band)
diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt
index 81217656..b3ef3aa2 100644
--- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt
+++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/WifiApManager.kt
@@ -52,6 +52,13 @@ object WifiApManager {
else -> false
}
+ @get:RequiresApi(30)
+ private val apMacRandomizationSupported by lazy {
+ WifiManager::class.java.getDeclaredMethod("isApMacRandomizationSupported")
+ }
+ @get:RequiresApi(30)
+ val isApMacRandomizationSupported get() = apMacRandomizationSupported(Services.wifi) as Boolean
+
private val getWifiApConfiguration by lazy { WifiManager::class.java.getDeclaredMethod("getWifiApConfiguration") }
@Suppress("DEPRECATION")
private val setWifiApConfiguration by lazy {
diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml
index f79491fc..69f3e8fc 100644
--- a/mobile/src/main/res/values-zh-rCN/strings.xml
+++ b/mobile/src/main/res/values-zh-rCN/strings.xml
@@ -70,6 +70,10 @@
- 已连接 %d 个设备
\n支持频道: %s
+ 随机接入点 MAC
+ 桥接 AP 并发
+ STA/AP 并发
+ STA/桥接 AP 并发
无
已屏蔽 %1$s:%2$s
复制 MAC
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index 73ca4bb6..c4ce4b36 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -83,6 +83,10 @@
- %1d clients connected
\nSupported channels: %s
+ Randomized AP MAC
+ Bridged AP concurrency
+ STA + AP concurrency
+ STA + Bridged AP concurrency
None
Blocked %1$s: %2$s
Copy MAC