Correctly handle TetherType changing

This commit is contained in:
Mygod
2020-05-29 21:20:53 -04:00
parent 299085293d
commit 069b32a7d9
7 changed files with 156 additions and 83 deletions

View File

@@ -1,6 +1,7 @@
package be.mygod.vpnhotspot.net
import android.content.res.Resources
import androidx.annotation.RequiresApi
import androidx.core.os.BuildCompat
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
@@ -25,39 +26,50 @@ enum class TetherType {
else -> false
}
companion object {
private val usbRegexs: List<Pattern>
private val wifiRegexs: List<Pattern>
private val wifiP2pRegexs: List<Pattern>
companion object : TetheringManager.TetheringEventCallback {
private lateinit var usbRegexs: List<Pattern>
private lateinit var wifiRegexs: List<Pattern>
private var wifiP2pRegexs = emptyList<Pattern>()
private val wimaxRegexs: List<Pattern>
private val bluetoothRegexs: List<Pattern>
private val ncmRegexs: List<Pattern>
private lateinit var bluetoothRegexs: List<Pattern>
private var ncmRegexs = emptyList<Pattern>()
private val ethernetRegex: Pattern?
private var requiresUpdate = true
private fun Pair<String?, Resources>.getRegexs(name: String) = second
.getStringArray(second.getIdentifier(name, "array", first))
.filterNotNull()
.map { it.toPattern() }
@RequiresApi(30)
private fun updateRegexs() {
requiresUpdate = false
val tethering = TetheringManager.PACKAGE to app.packageManager.getResourcesForApplication(
TetheringManager.resolvedService.serviceInfo.applicationInfo)
usbRegexs = tethering.getRegexs("config_tether_usb_regexs")
wifiRegexs = tethering.getRegexs("config_tether_wifi_regexs")
wifiP2pRegexs = tethering.getRegexs("config_tether_wifi_p2p_regexs")
bluetoothRegexs = tethering.getRegexs("config_tether_bluetooth_regexs")
ncmRegexs = tethering.getRegexs("config_tether_ncm_regexs")
}
@RequiresApi(30)
override fun onTetherableInterfaceRegexpsChanged() {
requiresUpdate = true
}
/**
* Source: https://android.googlesource.com/platform/frameworks/base/+/32e772f/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java#93
*/
init {
val system = "android" to Resources.getSystem()
if (BuildCompat.isAtLeastR()) {
val tethering = TetheringManager.PACKAGE to app.packageManager.getResourcesForApplication(
TetheringManager.resolvedService.serviceInfo.applicationInfo)
usbRegexs = tethering.getRegexs("config_tether_usb_regexs")
wifiRegexs = tethering.getRegexs("config_tether_wifi_regexs")
wifiP2pRegexs = tethering.getRegexs("config_tether_wifi_p2p_regexs")
bluetoothRegexs = tethering.getRegexs("config_tether_bluetooth_regexs")
ncmRegexs = tethering.getRegexs("config_tether_ncm_regexs")
TetheringManager.registerTetheringEventCallback(null, this)
updateRegexs()
} else {
usbRegexs = system.getRegexs("config_tether_usb_regexs")
wifiRegexs = system.getRegexs("config_tether_wifi_regexs")
wifiP2pRegexs = emptyList()
bluetoothRegexs = system.getRegexs("config_tether_bluetooth_regexs")
ncmRegexs = emptyList()
}
wimaxRegexs = system.getRegexs("config_tether_wimax_regexs")
// available since Android 4.0: https://android.googlesource.com/platform/frameworks/base/+/c96a667162fab44a250503caccb770109a9cb69a
@@ -66,11 +78,18 @@ enum class TetherType {
}
/**
* The result could change for the same interface since API 30+.
* It will be triggered by [TetheringManager.TetheringEventCallback.onTetherableInterfaceRegexpsChanged].
*
* Based on: https://android.googlesource.com/platform/frameworks/base/+/5d36f01/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java#479
*/
fun ofInterface(iface: String?, p2pDev: String? = null) = when {
tailrec fun ofInterface(iface: String?, p2pDev: String? = null): TetherType = when {
iface == null -> NONE
iface == p2pDev -> WIFI_P2P
requiresUpdate -> {
if (BuildCompat.isAtLeastR()) updateRegexs() else error("unexpected requiresUpdate")
ofInterface(iface, p2pDev)
}
wifiRegexs.any { it.matcher(iface).matches() } -> WIFI
wifiP2pRegexs.any { it.matcher(iface).matches() } -> WIFI_P2P
usbRegexs.any { it.matcher(iface).matches() } -> USB

View File

@@ -22,7 +22,9 @@ import be.mygod.vpnhotspot.util.ensureReceiverUnregistered
import com.android.dx.stock.ProxyBuilder
import timber.log.Timber
import java.lang.ref.WeakReference
import java.lang.reflect.InvocationHandler
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.concurrent.Executor
@@ -359,6 +361,8 @@ object TetheringManager {
* This will be called immediately after the callback is registered, and may be called
* multiple times later upon changes.
*
* CHANGED: This method will NOT be immediately called after registration.
*
* *@param reg The new regular expressions.
* @hide
*/
@@ -446,64 +450,61 @@ object TetheringManager {
* @param callback the callback to be called when tethering has change events.
*/
@RequiresApi(30)
fun registerTetheringEventCallback(executor: Executor, callback: TetheringEventCallback) {
fun registerTetheringEventCallback(executor: Executor?, callback: TetheringEventCallback) {
val reference = WeakReference(callback)
val proxy = synchronized(callbackMap) {
callbackMap.computeIfAbsent(callback) {
Proxy.newProxyInstance(interfaceTetheringEventCallback.classLoader,
arrayOf(interfaceTetheringEventCallback)) { proxy, method, args ->
@Suppress("NAME_SHADOWING") val callback = reference.get()
when (val name = method.name) {
"onTetheringSupported" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onTetheringSupported(args[0] as Boolean)
null
}
"onUpstreamChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onUpstreamChanged(args[0] as Network?)
null
}
"onTetherableInterfaceRegexpsChanged" -> {
callback?.onTetherableInterfaceRegexpsChanged()
null
}
"onTetherableInterfacesChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
@Suppress("UNCHECKED_CAST")
callback?.onTetherableInterfacesChanged(args[0] as List<String?>)
null
}
"onTetheredInterfacesChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
@Suppress("UNCHECKED_CAST")
callback?.onTetheredInterfacesChanged(args[0] as List<String?>)
null
}
"onError" -> {
if (args.size > 2) Timber.w("Unexpected args for $name: $args")
callback?.onError(args[0] as String, args[1] as Int)
null
}
"onClientsChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onClientsChanged(args[0] as Iterable<*>)
null
}
"onOffloadStatusChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onOffloadStatusChanged(args[0] as Int)
null
}
else -> {
Timber.w("Unexpected method, calling super: $method")
ProxyBuilder.callSuper(proxy, method, args)
arrayOf(interfaceTetheringEventCallback), object : InvocationHandler {
private var regexpsSent = false
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>): Any? {
@Suppress("NAME_SHADOWING") val callback = reference.get()
when (val name = method.name) {
"onTetheringSupported" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onTetheringSupported(args[0] as Boolean)
}
"onUpstreamChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onUpstreamChanged(args[0] as Network?)
}
"onTetherableInterfaceRegexpsChanged" -> {
if (regexpsSent) callback?.onTetherableInterfaceRegexpsChanged()
regexpsSent = true
}
"onTetherableInterfacesChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
@Suppress("UNCHECKED_CAST")
callback?.onTetherableInterfacesChanged(args[0] as List<String?>)
}
"onTetheredInterfacesChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
@Suppress("UNCHECKED_CAST")
callback?.onTetheredInterfacesChanged(args[0] as List<String?>)
}
"onError" -> {
if (args.size > 2) Timber.w("Unexpected args for $name: $args")
callback?.onError(args[0] as String, args[1] as Int)
}
"onClientsChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onClientsChanged(args[0] as Iterable<*>)
}
"onOffloadStatusChanged" -> {
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
callback?.onOffloadStatusChanged(args[0] as Int)
}
else -> {
Timber.w("Unexpected method, calling super: $method")
return ProxyBuilder.callSuper(proxy, method, args)
}
}
return null
}
}
})
}
}
registerTetheringEventCallback.invoke(instance, executor, proxy)
registerTetheringEventCallback.invoke(instance, executor ?: null.makeExecutor(), proxy)
}
/**
* Remove tethering event callback previously registered with