Support new fields in SoftApInfo

This commit is contained in:
Mygod
2021-05-26 20:56:44 -04:00
parent a69d635f93
commit 50de5a269c
7 changed files with 123 additions and 25 deletions

View File

@@ -10,6 +10,8 @@ import android.net.MacAddress
import android.os.Build
import android.os.Parcelable
import android.provider.Settings
import android.text.SpannableStringBuilder
import android.text.format.DateUtils
import android.view.View
import android.widget.Toast
import androidx.annotation.RequiresApi
@@ -29,6 +31,9 @@ import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat
import be.mygod.vpnhotspot.net.wifi.SoftApInfo
import be.mygod.vpnhotspot.net.wifi.WifiApManager
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.widget.SmartSnackbar
import kotlinx.coroutines.Dispatchers
@@ -188,28 +193,40 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
override val title get() = parent.getString(R.string.tethering_manage_wifi)
override val tetherType get() = TetherType.WIFI
override val type get() = VIEW_TYPE_WIFI
override val text get() = listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError,
info.run {
if (isEmpty()) null else joinToString("\n") @TargetApi(30) {
val info = SoftApInfo(it)
val frequency = info.frequency
parent.getString(R.string.tethering_manage_wifi_info, frequency,
SoftApConfigurationCompat.frequencyToChannel(frequency),
SoftApInfo.channelWidthLookup(info.bandwidth, true))
}
},
capability?.let { (maxSupportedClients, supportedFeatures) ->
app.resources.getQuantityString(R.plurals.tethering_manage_wifi_capabilities, maxSupportedClients,
numClients ?: "?", maxSupportedClients, sequence {
var features = supportedFeatures
if (features == 0L) yield(parent.getString(R.string.tethering_manage_wifi_no_features))
else while (features != 0L) {
val bit = features.takeLowestOneBit()
yield(WifiApManager.featureLookup(bit, true))
features = features and bit.inv()
override val text get() = parent.resources.configuration.locale.let { locale ->
listOfNotNull(failureReason?.let { WifiApManager.failureReasonLookup(it) }, baseError, info.run {
if (isEmpty()) null else joinToSpanned("\n") @TargetApi(30) { parcel ->
val info = SoftApInfo(parcel)
val frequency = info.frequency
val channel = SoftApConfigurationCompat.frequencyToChannel(frequency)
val bandwidth = SoftApInfo.channelWidthLookup(info.bandwidth, true)
if (BuildCompat.isAtLeastS()) {
var bssid = makeMacSpan(info.bssid.toString())
info.apInstanceIdentifier?.let { // take the fast route if possible
bssid = if (bssid is String) "$bssid%$it" else SpannableStringBuilder(bssid).append("%$it")
}
}.joinToString())
}).joinToString("\n")
val timeout = info.autoShutdownTimeoutMillis
parent.getText(if (timeout == 0L) {
R.string.tethering_manage_wifi_info_timeout_disabled
} else R.string.tethering_manage_wifi_info_timeout_enabled).format(locale,
frequency, channel, bandwidth, bssid, info.wifiStandard,
// http://unicode.org/cldr/trac/ticket/3407
DateUtils.formatElapsedTime(timeout / 1000))
} else parent.getText(R.string.tethering_manage_wifi_info).format(locale,
frequency, channel, bandwidth)
}
}, capability?.let { (maxSupportedClients, supportedFeatures) ->
app.resources.getQuantityText(R.plurals.tethering_manage_wifi_capabilities,
maxSupportedClients).format(locale, numClients ?: "?", maxSupportedClients, sequence {
var features = supportedFeatures
if (features != 0L) while (features != 0L) {
val bit = features.takeLowestOneBit()
yield(WifiApManager.featureLookup(bit, true))
features = features and bit.inv()
} else yield(parent.getText(R.string.tethering_manage_wifi_no_features))
}.joinToSpanned())
}).joinToSpanned("\n")
}
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_WIFI, true, this)
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_WIFI, this::onException)

View File

@@ -1,20 +1,47 @@
package be.mygod.vpnhotspot.net.wifi
import android.annotation.TargetApi
import android.net.MacAddress
import android.os.Parcelable
import androidx.annotation.RequiresApi
import be.mygod.vpnhotspot.util.ConstantLookup
import be.mygod.vpnhotspot.util.UnblockCentral
import timber.log.Timber
@JvmInline
@RequiresApi(30)
value class SoftApInfo(val inner: Parcelable) {
companion object {
private val classSoftApInfo by lazy { Class.forName("android.net.wifi.SoftApInfo") }
private val getFrequency by lazy { classSoftApInfo.getDeclaredMethod("getFrequency") }
private val getBandwidth by lazy { classSoftApInfo.getDeclaredMethod("getBandwidth") }
private val clazz by lazy { Class.forName("android.net.wifi.SoftApInfo") }
private val getFrequency by lazy { clazz.getDeclaredMethod("getFrequency") }
private val getBandwidth by lazy { clazz.getDeclaredMethod("getBandwidth") }
@get:RequiresApi(31)
private val getBssid by lazy { clazz.getDeclaredMethod("getBssid") }
@get:RequiresApi(31)
private val getWifiStandard by lazy { clazz.getDeclaredMethod("getWifiStandard") }
@get:RequiresApi(31)
private val getApInstanceIdentifier by lazy @TargetApi(31) {
UnblockCentral.SoftApInfo_getApInstanceIdentifier(clazz)
}
@get:RequiresApi(31)
private val getAutoShutdownTimeoutMillis by lazy { clazz.getDeclaredMethod("getAutoShutdownTimeoutMillis") }
val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { classSoftApInfo }
val channelWidthLookup = ConstantLookup("CHANNEL_WIDTH_") { clazz }
}
val frequency get() = getFrequency.invoke(inner) as Int
val bandwidth get() = getBandwidth.invoke(inner) as Int
@get:RequiresApi(31)
val bssid get() = getBssid.invoke(inner) as MacAddress
@get:RequiresApi(31)
val wifiStandard get() = getWifiStandard.invoke(inner) as Int
@get:RequiresApi(31)
val apInstanceIdentifier get() = try {
getApInstanceIdentifier.invoke(inner) as? String
} catch (e: ReflectiveOperationException) {
Timber.w(e)
null
}
@get:RequiresApi(31)
val autoShutdownTimeoutMillis get() = getAutoShutdownTimeoutMillis.invoke(inner) as Long
}

View File

@@ -0,0 +1,32 @@
package be.mygod.vpnhotspot.util
import android.annotation.SuppressLint
import androidx.annotation.RequiresApi
import timber.log.Timber
/**
* The central object for accessing all the useful blocked APIs. Thanks Google!
*/
@SuppressLint("DiscouragedPrivateApi")
@Suppress("FunctionName")
object UnblockCentral {
/**
* Retrieve this property before doing dangerous shit.
*/
@get:RequiresApi(28)
private val init by lazy {
try {
Class.forName("dalvik.system.VMDebug").getDeclaredMethod("allowHiddenApiReflectionFrom", Class::class.java)
.invoke(null, UnblockCentral::class.java)
true
} catch (e: ReflectiveOperationException) {
Timber.w(e)
false
}
}
@RequiresApi(31)
fun SoftApInfo_getApInstanceIdentifier(clazz: Class<*>) = init.let {
clazz.getDeclaredMethod("getApInstanceIdentifier")
}
}

View File

@@ -126,6 +126,15 @@ fun CharSequence.format(locale: Locale, vararg args: Any) = SpannableStringBuild
}
}
fun <T> Iterable<T>.joinToSpanned(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "",
limit: Int = -1, truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null) =
joinTo(SpannableStringBuilder(), separator, prefix, postfix, limit, truncated, transform)
fun <T> Sequence<T>.joinToSpanned(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "",
limit: Int = -1, truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null) =
joinTo(SpannableStringBuilder(), separator, prefix, postfix, limit, truncated, transform)
fun makeIpSpan(ip: InetAddress) = ip.hostAddress.let {
// exclude all bogon IP addresses supported by Android APIs
if (!app.hasTouch || ip.isMulticastAddress || ip.isAnyLocalAddress || ip.isLoopbackAddress ||