Make InvocationHandler more robust
This commit is contained in:
@@ -71,9 +71,9 @@ enum class TetherType(@DrawableRes val icon: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
override fun onTetherableInterfaceRegexpsChanged(args: Array<out Any?>?) = synchronized(this) {
|
override fun onTetherableInterfaceRegexpsChanged(reg: Any?) = synchronized(this) {
|
||||||
if (requiresUpdate) return@synchronized
|
if (requiresUpdate) return@synchronized
|
||||||
Timber.i("onTetherableInterfaceRegexpsChanged: ${args?.contentDeepToString()}")
|
Timber.i("onTetherableInterfaceRegexpsChanged: $reg")
|
||||||
TetheringManager.unregisterTetheringEventCallback(this)
|
TetheringManager.unregisterTetheringEventCallback(this)
|
||||||
requiresUpdate = true
|
requiresUpdate = true
|
||||||
listener()
|
listener()
|
||||||
|
|||||||
@@ -251,13 +251,12 @@ object TetheringManager {
|
|||||||
val proxy = ProxyBuilder.forClass(classOnStartTetheringCallback).apply {
|
val proxy = ProxyBuilder.forClass(classOnStartTetheringCallback).apply {
|
||||||
dexCache(cacheDir)
|
dexCache(cacheDir)
|
||||||
handler { proxy, method, args ->
|
handler { proxy, method, args ->
|
||||||
if (args.isNotEmpty()) Timber.w("Unexpected args for ${method.name}: $args")
|
|
||||||
@Suppress("NAME_SHADOWING") val callback = reference.get()
|
@Suppress("NAME_SHADOWING") val callback = reference.get()
|
||||||
when (method.name) {
|
if (args.isEmpty()) when (method.name) {
|
||||||
"onTetheringStarted" -> callback?.onTetheringStarted()
|
"onTetheringStarted" -> return@handler callback?.onTetheringStarted()
|
||||||
"onTetheringFailed" -> callback?.onTetheringFailed()
|
"onTetheringFailed" -> return@handler callback?.onTetheringFailed()
|
||||||
else -> ProxyBuilder.callSuper(proxy, method, args)
|
|
||||||
}
|
}
|
||||||
|
ProxyBuilder.callSuper(proxy, method, args)
|
||||||
}
|
}
|
||||||
}.build()
|
}.build()
|
||||||
startTetheringLegacy(Services.connectivity, type, showProvisioningUi, proxy, handler)
|
startTetheringLegacy(Services.connectivity, type, showProvisioningUi, proxy, handler)
|
||||||
@@ -279,15 +278,9 @@ object TetheringManager {
|
|||||||
arrayOf(interfaceStartTetheringCallback), object : InvocationHandler {
|
arrayOf(interfaceStartTetheringCallback), object : InvocationHandler {
|
||||||
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
|
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
|
||||||
@Suppress("NAME_SHADOWING") val callback = reference.get()
|
@Suppress("NAME_SHADOWING") val callback = reference.get()
|
||||||
return when (val name = method.name) {
|
return when {
|
||||||
"onTetheringStarted" -> {
|
method.matches("onTetheringStarted") -> callback?.onTetheringStarted()
|
||||||
if (!args.isNullOrEmpty()) Timber.w("Unexpected args for $name: $args")
|
method.matches1<Int>("onTetheringFailed") -> callback?.onTetheringFailed(args?.get(0) as Int)
|
||||||
callback?.onTetheringStarted()
|
|
||||||
}
|
|
||||||
"onTetheringFailed" -> {
|
|
||||||
if (args?.size != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
callback?.onTetheringFailed(args?.get(0) as Int)
|
|
||||||
}
|
|
||||||
else -> callSuper(interfaceStartTetheringCallback, proxy, method, args)
|
else -> callSuper(interfaceStartTetheringCallback, proxy, method, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +442,7 @@ object TetheringManager {
|
|||||||
* *@param reg The new regular expressions.
|
* *@param reg The new regular expressions.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
fun onTetherableInterfaceRegexpsChanged(args: Array<out Any?>?) {}
|
fun onTetherableInterfaceRegexpsChanged(reg: Any?) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when there was a change in the list of tetherable interfaces. Tetherable
|
* Called when there was a change in the list of tetherable interfaces. Tetherable
|
||||||
@@ -545,40 +538,34 @@ object TetheringManager {
|
|||||||
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
|
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val callback = reference.get()
|
val callback = reference.get()
|
||||||
val noArgs = args?.size ?: 0
|
return when {
|
||||||
return when (val name = method.name) {
|
method.matches1<Boolean>("onTetheringSupported") -> {
|
||||||
"onTetheringSupported" -> {
|
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
callback?.onTetheringSupported(args!![0] as Boolean)
|
callback?.onTetheringSupported(args!![0] as Boolean)
|
||||||
}
|
}
|
||||||
"onUpstreamChanged" -> {
|
method.matches1<Network>("onUpstreamChanged") -> {
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
callback?.onUpstreamChanged(args!![0] as Network?)
|
callback?.onUpstreamChanged(args!![0] as Network?)
|
||||||
}
|
}
|
||||||
"onTetherableInterfaceRegexpsChanged" -> {
|
method.name == "onTetherableInterfaceRegexpsChanged" &&
|
||||||
if (regexpsSent) callback?.onTetherableInterfaceRegexpsChanged(args)
|
method.parameters.singleOrNull()?.type?.name ==
|
||||||
|
"android.net.TetheringManager\$TetheringInterfaceRegexps" -> {
|
||||||
|
if (regexpsSent) callback?.onTetherableInterfaceRegexpsChanged(args!!.single())
|
||||||
regexpsSent = true
|
regexpsSent = true
|
||||||
}
|
}
|
||||||
"onTetherableInterfacesChanged" -> {
|
method.matches1<java.util.List<*>>("onTetherableInterfacesChanged") -> {
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
callback?.onTetherableInterfacesChanged(args!![0] as List<String?>)
|
callback?.onTetherableInterfacesChanged(args!![0] as List<String?>)
|
||||||
}
|
}
|
||||||
"onTetheredInterfacesChanged" -> {
|
method.matches1<java.util.List<*>>("onTetheredInterfacesChanged") -> {
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
callback?.onTetheredInterfacesChanged(args!![0] as List<String?>)
|
callback?.onTetheredInterfacesChanged(args!![0] as List<String?>)
|
||||||
}
|
}
|
||||||
"onError" -> {
|
method.matches2<String, Int>("onError") -> {
|
||||||
if (noArgs != 2) Timber.w("Unexpected args for $name: $args")
|
|
||||||
callback?.onError(args!![0] as String, args[1] as Int)
|
callback?.onError(args!![0] as String, args[1] as Int)
|
||||||
}
|
}
|
||||||
"onClientsChanged" -> {
|
method.matches1<java.util.Collection<*>>("onClientsChanged") -> {
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
callback?.onClientsChanged(args!![0] as Collection<*>)
|
callback?.onClientsChanged(args!![0] as Collection<*>)
|
||||||
}
|
}
|
||||||
"onOffloadStatusChanged" -> {
|
method.matches1<Int>("onOffloadStatusChanged") -> {
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: $args")
|
|
||||||
callback?.onOffloadStatusChanged(args!![0] as Int)
|
callback?.onOffloadStatusChanged(args!![0] as Int)
|
||||||
}
|
}
|
||||||
else -> callSuper(interfaceTetheringEventCallback, proxy, method, args)
|
else -> callSuper(interfaceTetheringEventCallback, proxy, method, args)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import be.mygod.vpnhotspot.util.LongConstantLookup
|
|||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
value class SoftApCapability(val inner: Parcelable) {
|
value class SoftApCapability(val inner: Parcelable) {
|
||||||
companion object {
|
companion object {
|
||||||
private val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") }
|
val clazz by lazy { Class.forName("android.net.wifi.SoftApCapability") }
|
||||||
private val getMaxSupportedClients by lazy { clazz.getDeclaredMethod("getMaxSupportedClients") }
|
private val getMaxSupportedClients by lazy { clazz.getDeclaredMethod("getMaxSupportedClients") }
|
||||||
private val areFeaturesSupported by lazy { clazz.getDeclaredMethod("areFeaturesSupported", Long::class.java) }
|
private val areFeaturesSupported by lazy { clazz.getDeclaredMethod("areFeaturesSupported", Long::class.java) }
|
||||||
@get:RequiresApi(31)
|
@get:RequiresApi(31)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import timber.log.Timber
|
|||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
value class SoftApInfo(val inner: Parcelable) {
|
value class SoftApInfo(val inner: Parcelable) {
|
||||||
companion object {
|
companion object {
|
||||||
private val clazz by lazy { Class.forName("android.net.wifi.SoftApInfo") }
|
val clazz by lazy { Class.forName("android.net.wifi.SoftApInfo") }
|
||||||
private val getFrequency by lazy { clazz.getDeclaredMethod("getFrequency") }
|
private val getFrequency by lazy { clazz.getDeclaredMethod("getFrequency") }
|
||||||
private val getBandwidth by lazy { clazz.getDeclaredMethod("getBandwidth") }
|
private val getBandwidth by lazy { clazz.getDeclaredMethod("getBandwidth") }
|
||||||
@get:RequiresApi(31)
|
@get:RequiresApi(31)
|
||||||
|
|||||||
@@ -10,13 +10,9 @@ import android.os.Build
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.os.BuildCompat
|
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat
|
import be.mygod.vpnhotspot.net.wifi.SoftApConfigurationCompat.Companion.toCompat
|
||||||
import be.mygod.vpnhotspot.util.ConstantLookup
|
import be.mygod.vpnhotspot.util.*
|
||||||
import be.mygod.vpnhotspot.util.Services
|
|
||||||
import be.mygod.vpnhotspot.util.callSuper
|
|
||||||
import be.mygod.vpnhotspot.util.findIdentifier
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.lang.reflect.InvocationHandler
|
import java.lang.reflect.InvocationHandler
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
@@ -260,55 +256,41 @@ object WifiApManager {
|
|||||||
} else invokeActual(proxy, method, args)
|
} else invokeActual(proxy, method, args)
|
||||||
|
|
||||||
private fun invokeActual(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
|
private fun invokeActual(proxy: Any, method: Method, args: Array<out Any?>?): Any? {
|
||||||
val noArgs = args?.size ?: 0
|
return when {
|
||||||
return when (val name = method.name) {
|
method.matches2<Int, Int>("onStateChanged") -> {
|
||||||
"onStateChanged" -> {
|
|
||||||
if (noArgs != 2) Timber.w("Unexpected args for $name: ${args?.contentToString()}")
|
|
||||||
callback.onStateChanged(args!![0] as Int, args[1] as Int)
|
callback.onStateChanged(args!![0] as Int, args[1] as Int)
|
||||||
}
|
}
|
||||||
"onNumClientsChanged" -> @Suppress("DEPRECATION") {
|
method.matches1<Int>("onNumClientsChanged") -> @Suppress("DEPRECATION") {
|
||||||
if (Build.VERSION.SDK_INT >= 30) Timber.w(Exception("Unexpected onNumClientsChanged"))
|
if (Build.VERSION.SDK_INT >= 30) Timber.w(Exception("Unexpected onNumClientsChanged"))
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}")
|
|
||||||
callback.onNumClientsChanged(args!![0] as Int)
|
callback.onNumClientsChanged(args!![0] as Int)
|
||||||
}
|
}
|
||||||
"onConnectedClientsChanged" -> @TargetApi(30) {
|
method.matches1<java.util.List<*>>("onConnectedClientsChanged") -> @TargetApi(30) {
|
||||||
if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onConnectedClientsChanged"))
|
if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onConnectedClientsChanged"))
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
when (noArgs) {
|
callback.onConnectedClientsChanged(args!![0] as List<Parcelable>)
|
||||||
1 -> callback.onConnectedClientsChanged(args!![0] as List<Parcelable>)
|
|
||||||
2 -> null // we use the old method which returns all clients in one call
|
|
||||||
else -> {
|
|
||||||
Timber.w("Unexpected args for $name: ${args?.contentToString()}")
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
method.matches1<java.util.List<*>>("onInfoChanged") -> @TargetApi(31) {
|
||||||
}
|
|
||||||
"onInfoChanged" -> @TargetApi(30) {
|
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}")
|
|
||||||
val arg = args!![0]
|
|
||||||
if (arg is List<*>) {
|
|
||||||
if (Build.VERSION.SDK_INT < 31) Timber.w(Exception("Unexpected onInfoChanged API 31+"))
|
if (Build.VERSION.SDK_INT < 31) Timber.w(Exception("Unexpected onInfoChanged API 31+"))
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
callback.onInfoChanged(arg as List<Parcelable>)
|
callback.onInfoChanged(args!![0] as List<Parcelable>)
|
||||||
} else {
|
}
|
||||||
|
method.matches("onInfoChanged", SoftApInfo.clazz) -> @TargetApi(30) {
|
||||||
when (Build.VERSION.SDK_INT) {
|
when (Build.VERSION.SDK_INT) {
|
||||||
30 -> { }
|
30 -> { }
|
||||||
in 31..Int.MAX_VALUE -> return null // ignore old version calls
|
in 31..Int.MAX_VALUE -> return null // ignore old version calls
|
||||||
else -> Timber.w(Exception("Unexpected onInfoChanged API 30"))
|
else -> Timber.w(Exception("Unexpected onInfoChanged API 30"))
|
||||||
}
|
}
|
||||||
|
val arg = args!![0]
|
||||||
val info = SoftApInfo(arg as Parcelable)
|
val info = SoftApInfo(arg as Parcelable)
|
||||||
callback.onInfoChanged( // check for legacy empty info with CHANNEL_WIDTH_INVALID
|
callback.onInfoChanged( // check for legacy empty info with CHANNEL_WIDTH_INVALID
|
||||||
if (info.frequency == 0 && info.bandwidth == 0) emptyList() else listOf(arg))
|
if (info.frequency == 0 && info.bandwidth == 0) emptyList() else listOf(arg))
|
||||||
}
|
}
|
||||||
}
|
method.matches("onCapabilityChanged", SoftApCapability.clazz) -> @TargetApi(30) {
|
||||||
"onCapabilityChanged" -> @TargetApi(30) {
|
|
||||||
if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged"))
|
if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onCapabilityChanged"))
|
||||||
if (noArgs != 1) Timber.w("Unexpected args for $name: ${args?.contentToString()}")
|
|
||||||
callback.onCapabilityChanged(args!![0] as Parcelable)
|
callback.onCapabilityChanged(args!![0] as Parcelable)
|
||||||
}
|
}
|
||||||
"onBlockedClientConnecting" -> @TargetApi(30) {
|
method.matches("onBlockedClientConnecting", WifiClient.clazz, Int::class.java) -> @TargetApi(30) {
|
||||||
if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onBlockedClientConnecting"))
|
if (Build.VERSION.SDK_INT < 30) Timber.w(Exception("Unexpected onBlockedClientConnecting"))
|
||||||
if (noArgs != 2) Timber.w("Unexpected args for $name: ${args?.contentToString()}")
|
|
||||||
callback.onBlockedClientConnecting(args!![0] as Parcelable, args[1] as Int)
|
callback.onBlockedClientConnecting(args!![0] as Parcelable, args[1] as Int)
|
||||||
}
|
}
|
||||||
else -> callSuper(interfaceSoftApCallback, proxy, method, args)
|
else -> callSuper(interfaceSoftApCallback, proxy, method, args)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import timber.log.Timber
|
|||||||
@RequiresApi(30)
|
@RequiresApi(30)
|
||||||
value class WifiClient(val inner: Parcelable) {
|
value class WifiClient(val inner: Parcelable) {
|
||||||
companion object {
|
companion object {
|
||||||
private val clazz by lazy { Class.forName("android.net.wifi.WifiClient") }
|
val clazz by lazy { Class.forName("android.net.wifi.WifiClient") }
|
||||||
private val getMacAddress by lazy { clazz.getDeclaredMethod("getMacAddress") }
|
private val getMacAddress by lazy { clazz.getDeclaredMethod("getMacAddress") }
|
||||||
@get:RequiresApi(31)
|
@get:RequiresApi(31)
|
||||||
private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) }
|
private val getApInstanceIdentifier by lazy @TargetApi(31) { UnblockCentral.getApInstanceIdentifier(clazz) }
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import androidx.annotation.RequiresApi
|
|||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||||
import be.mygod.vpnhotspot.util.callSuper
|
import be.mygod.vpnhotspot.util.callSuper
|
||||||
|
import be.mygod.vpnhotspot.util.matches1
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import timber.log.Timber
|
|
||||||
import java.lang.reflect.InvocationHandler
|
import java.lang.reflect.InvocationHandler
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.lang.reflect.Proxy
|
import java.lang.reflect.Proxy
|
||||||
@@ -117,9 +117,8 @@ object WifiP2pManagerHelper {
|
|||||||
val result = CompletableDeferred<Collection<WifiP2pGroup>>()
|
val result = CompletableDeferred<Collection<WifiP2pGroup>>()
|
||||||
requestPersistentGroupInfo(this, c, Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader,
|
requestPersistentGroupInfo(this, c, Proxy.newProxyInstance(interfacePersistentGroupInfoListener.classLoader,
|
||||||
arrayOf(interfacePersistentGroupInfoListener), object : InvocationHandler {
|
arrayOf(interfacePersistentGroupInfoListener), object : InvocationHandler {
|
||||||
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? = when (method.name) {
|
override fun invoke(proxy: Any, method: Method, args: Array<out Any?>?): Any? = when {
|
||||||
"onPersistentGroupInfoAvailable" -> {
|
method.matches1<java.util.Collection<*>>("onPersistentGroupInfoAvailable") -> {
|
||||||
if (args?.size != 1) Timber.w(IllegalArgumentException("Unexpected args: $args"))
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
result.complete(getGroupList(args!![0]) as Collection<WifiP2pGroup>)
|
result.complete(getGroupList(args!![0]) as Collection<WifiP2pGroup>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,14 @@ fun Long.toPluralInt(): Int {
|
|||||||
return (this % 1000000000).toInt() + 1000000000
|
return (this % 1000000000).toInt() + 1000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun Method.matches(name: String, vararg classes: Class<*>) = this.name == name && parameterCount == classes.size &&
|
||||||
|
(0 until parameterCount).all { i -> parameters[i].type == classes[i] }
|
||||||
|
@RequiresApi(26)
|
||||||
|
inline fun <reified T> Method.matches1(name: String) = matches(name, T::class.java)
|
||||||
|
@RequiresApi(26)
|
||||||
|
inline fun <reified T0, reified T1> Method.matches2(name: String) = matches(name, T0::class.java, T1::class.java)
|
||||||
|
|
||||||
fun Context.ensureReceiverUnregistered(receiver: BroadcastReceiver) {
|
fun Context.ensureReceiverUnregistered(receiver: BroadcastReceiver) {
|
||||||
try {
|
try {
|
||||||
unregisterReceiver(receiver)
|
unregisterReceiver(receiver)
|
||||||
|
|||||||
Reference in New Issue
Block a user