From b38498071f6c5063772d87425d7c63e72f6a0a35 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 29 May 2021 17:50:31 -0400 Subject: [PATCH] Support showing supported channels --- README.md | 6 +++ .../mygod/vpnhotspot/manage/TetherManager.kt | 53 +++++++++++++++++-- .../vpnhotspot/net/wifi/SoftApCapability.kt | 16 +++++- .../net/wifi/SoftApConfigurationCompat.kt | 14 +++++ .../vpnhotspot/net/wifi/WifiApManager.kt | 3 +- .../mygod/vpnhotspot/util/ConstantLookup.kt | 8 +-- .../mygod/vpnhotspot/util/UnblockCentral.kt | 6 +++ mobile/src/main/res/values-zh-rCN/strings.xml | 1 + mobile/src/main/res/values/strings.xml | 1 + 9 files changed, 96 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6ddee0ce..58454519 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/ConnectivityModuleConnector;->IN_PROCESS_SUFFIX:Ljava/lang/String;` * (since API 30) `Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked` * (since API 30) `Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked` +* (since API 31) `Landroid/net/wifi/SoftApConfiguration;->BAND_TYPES:[I,blocked` * (since API 31) `Landroid/net/wifi/SoftApInfo;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (since API 31) `Landroid/net/wifi/WifiClient;->getApInstanceIdentifier()Ljava/lang/String;,blocked` * (prior to API 30) `Landroid/net/wifi/WifiConfiguration$KeyMgmt;->FT_PSK:I,lo-prio,max-target-o` @@ -228,9 +229,14 @@ Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded * (since API 30) `Landroid/net/TetheringManager;->startTethering(Landroid/net/TetheringManager$TetheringRequest;Ljava/util/concurrent/Executor;Landroid/net/TetheringManager$StartTetheringCallback;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/TetheringManager;->stopTethering(I)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/TetheringManager;->unregisterTetheringEventCallback(Landroid/net/TetheringManager$TetheringEventCallback;)V,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_24G_SUPPORTED:J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_5G_SUPPORTED:J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_60G_SUPPORTED:J,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_BAND_6G_SUPPORTED:J,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApCapability;->SOFTAP_FEATURE_*:J,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApCapability;->areFeaturesSupported(J)Z,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApCapability;->getMaxSupportedClients()I,sdk,system-api,test-api` +* (since API 31) `Landroid/net/wifi/SoftApCapability;->getSupportedChannelList(I)[I,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->()V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->(Landroid/net/wifi/SoftApConfiguration;)V,sdk,system-api,test-api` * (since API 30) `Landroid/net/wifi/SoftApConfiguration$Builder;->build()Landroid/net/wifi/SoftApConfiguration;,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 0545f87c..d7c8b491 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetherManager.kt @@ -199,13 +199,23 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), override val type get() = VIEW_TYPE_WIFI @TargetApi(30) - private fun formatCapability(locale: Locale) = capability?.let { - val capability = SoftApCapability(it) + private fun formatCapability(locale: Locale) = capability?.let { parcel -> + val capability = SoftApCapability(parcel) val numClients = numClients val maxClients = capability.maxSupportedClients - val supportedFeatures = capability.supportedFeatures - app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities, numClients ?: 0).format(locale, - numClients ?: "?", maxClients, sequence { + var supportedFeatures = 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, + SoftApCapability.SOFTAP_FEATURE_BAND_6G_SUPPORTED to SoftApConfigurationCompat.BAND_6GHZ, + SoftApCapability.SOFTAP_FEATURE_BAND_60G_SUPPORTED to SoftApConfigurationCompat.BAND_60GHZ, + )) { + if (capability.getSupportedChannelList(band).isEmpty()) continue + // reduce double reporting + supportedFeatures = supportedFeatures 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 (features != 0L) while (features != 0L) { val bit = features.takeLowestOneBit() @@ -213,6 +223,39 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(), features = features and bit.inv() } else yield(parent.getText(R.string.tethering_manage_wifi_no_features)) }.joinToSpanned()) + if (BuildCompat.isAtLeastS()) { + val list = SoftApConfigurationCompat.BAND_TYPES.map { band -> + val channels = capability.getSupportedChannelList(band) + if (channels.isNotEmpty()) StringBuilder().apply { + append(SoftApConfigurationCompat.bandLookup(band, true)) + append(" (") + channels.sort() + var pending: Int? = null + var last = channels[0] + append(last) + for (channel in channels.asSequence().drop(1)) { + if (channel == last + 1) pending = channel else { + pending?.let { + append('-') + append(it) + pending = null + } + append(',') + append(channel) + } + last = channel + } + pending?.let { + append('-') + append(it) + } + append(')') + } else null + }.filterNotNull() + if (list.isNotEmpty()) result.append(parent.getText(R.string.tethering_manage_wifi_supported_channels) + .format(locale, list.joinToString("; "))) + } + result } ?: numClients?.let { numClients -> app.resources.getQuantityText(R.plurals.tethering_manage_wifi_clients, numClients).format(locale, numClients) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt index 85d0c057..24432405 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApCapability.kt @@ -11,10 +11,23 @@ value class SoftApCapability(val inner: Parcelable) { private val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") } private val getMaxSupportedClients by lazy { clazz.getDeclaredMethod("getMaxSupportedClients") } private val areFeaturesSupported by lazy { clazz.getDeclaredMethod("areFeaturesSupported", Long::class.java) } + @get:RequiresApi(31) + private val getSupportedChannelList by lazy { + clazz.getDeclaredMethod("getSupportedChannelList", Int::class.java) + } + + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_24G_SUPPORTED = 32L + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_5G_SUPPORTED = 64L + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_6G_SUPPORTED = 128L + @RequiresApi(31) + const val SOFTAP_FEATURE_BAND_60G_SUPPORTED = 256L val featureLookup by lazy { LongConstantLookup(clazz, "SOFTAP_FEATURE_") } } - val maxSupportedClients get() = getMaxSupportedClients.invoke(inner) as Int + val maxSupportedClients get() = getMaxSupportedClients(inner) as Int val supportedFeatures: Long get() { var supportedFeatures = 0L var probe = 1L @@ -24,4 +37,5 @@ value class SoftApCapability(val inner: Parcelable) { } return supportedFeatures } + fun getSupportedChannelList(band: Int) = getSupportedChannelList(inner, band) as IntArray } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt index b0f62894..e867b361 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/wifi/SoftApConfigurationCompat.kt @@ -12,7 +12,10 @@ import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.net.MacAddressCompat import be.mygod.vpnhotspot.net.MacAddressCompat.Companion.toCompat import be.mygod.vpnhotspot.net.monitor.TetherTimeoutMonitor +import be.mygod.vpnhotspot.util.ConstantLookup +import be.mygod.vpnhotspot.util.UnblockCentral import kotlinx.parcelize.Parcelize +import timber.log.Timber @Parcelize data class SoftApConfigurationCompat( @@ -40,10 +43,21 @@ data class SoftApConfigurationCompat( companion object { const val BAND_2GHZ = 1 const val BAND_5GHZ = 2 + @TargetApi(30) const val BAND_6GHZ = 4 + @TargetApi(31) const val BAND_60GHZ = 8 private const val BAND_LEGACY = BAND_2GHZ or BAND_5GHZ const val BAND_ANY = BAND_LEGACY or BAND_6GHZ + val BAND_TYPES by lazy { + if (BuildCompat.isAtLeastS()) try { + return@lazy UnblockCentral.SoftApConfiguration_BAND_TYPES + } catch (e: ReflectiveOperationException) { + Timber.w(e) + } + intArrayOf(BAND_2GHZ, BAND_5GHZ, BAND_6GHZ, BAND_60GHZ) + } + val bandLookup = ConstantLookup("BAND_", null, "2GHZ", "5GHZ") fun isLegacyEitherBand(band: Int) = band and BAND_LEGACY == BAND_LEGACY 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 707fa807..b7fc93b5 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 @@ -122,8 +122,7 @@ object WifiApManager { fun onBlockedClientConnecting(client: Parcelable, blockedReason: Int) { } } @RequiresApi(28) - val failureReasonLookup = ConstantLookup("SAP_START_FAILURE_", - "SAP_START_FAILURE_GENERAL", "SAP_START_FAILURE_NO_CHANNEL") + val failureReasonLookup = ConstantLookup("SAP_START_FAILURE_", "GENERAL", "NO_CHANNEL") @get:RequiresApi(30) val clientBlockLookup by lazy { ConstantLookup("SAP_CLIENT_") } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt index c7d3fb17..0e18da97 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/ConstantLookup.kt @@ -7,7 +7,7 @@ import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R import timber.log.Timber -class ConstantLookup(private val prefix: String, private val lookup29: Array, +class ConstantLookup(private val prefix: String, private val lookup29: Array, private val clazz: () -> Class<*>) { private val lookup by lazy { SparseArrayCompat().apply { @@ -25,16 +25,16 @@ class ConstantLookup(private val prefix: String, private val lookup29: Array Class<*>) = +fun ConstantLookup(prefix: String, vararg lookup29: String?, clazz: () -> Class<*>) = ConstantLookup(prefix, lookup29, clazz) @Suppress("FunctionName") -inline fun ConstantLookup(prefix: String, vararg lookup29: String) = +inline fun ConstantLookup(prefix: String, vararg lookup29: String?) = ConstantLookup(prefix, lookup29) { T::class.java } class LongConstantLookup(private val clazz: Class<*>, private val prefix: String) { diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt index 2813221b..81e776a7 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/UnblockCentral.kt @@ -1,6 +1,7 @@ package be.mygod.vpnhotspot.util import android.annotation.SuppressLint +import android.net.wifi.SoftApConfiguration import android.net.wifi.p2p.WifiP2pConfig import androidx.annotation.RequiresApi import timber.log.Timber @@ -28,6 +29,11 @@ object UnblockCentral { } } + @get:RequiresApi(31) + val SoftApConfiguration_BAND_TYPES get() = init.let { + SoftApConfiguration::class.java.getField("BAND_TYPES").get(null) as IntArray + } + @RequiresApi(31) fun getApInstanceIdentifier(clazz: Class<*>) = init.let { clazz.getDeclaredMethod("getApInstanceIdentifier") } diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml index 469b1b00..f6586d35 100644 --- a/mobile/src/main/res/values-zh-rCN/strings.xml +++ b/mobile/src/main/res/values-zh-rCN/strings.xml @@ -69,6 +69,7 @@ 已连接 %d 个设备 + \n支持频道: %s 已屏蔽 %1$s:%2$s 复制 MAC diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index c3787e81..2b6b9933 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -82,6 +82,7 @@ %d client connected %1d clients connected + \nSupported channels: %s None Blocked %1$s: %2$s Copy MAC