Correctly handle TetherType changing
This commit is contained in:
@@ -2,14 +2,18 @@ package be.mygod.vpnhotspot
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.os.Build
|
||||
import androidx.core.os.BuildCompat
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.net.Routing
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import timber.log.Timber
|
||||
import java.net.NetworkInterface
|
||||
|
||||
abstract class RoutingManager(private val caller: Any, val downstream: String, private val isWifi: Boolean) {
|
||||
abstract class RoutingManager(private val caller: Any, val downstream: String,
|
||||
private val forceWifi: Boolean = false) : TetheringManager.TetheringEventCallback {
|
||||
companion object {
|
||||
private const val KEY_MASQUERADE_MODE = "service.masqueradeMode"
|
||||
var masqueradeMode: Routing.MasqueradeMode
|
||||
@@ -54,10 +58,12 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
||||
|
||||
val started get() = active[downstream] === this
|
||||
private var routing: Routing? = null
|
||||
private var isWifi = forceWifi || TetherType.ofInterface(downstream).isWifi
|
||||
|
||||
fun start() = when (val other = active.putIfAbsent(downstream, this)) {
|
||||
null -> {
|
||||
if (isWifi) WifiDoubleLock.acquire(this)
|
||||
if (!forceWifi && BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this)
|
||||
initRouting()
|
||||
}
|
||||
this -> true // already started
|
||||
@@ -66,6 +72,13 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
||||
|
||||
open fun ifaceHandler(iface: NetworkInterface) { }
|
||||
|
||||
override fun onTetherableInterfaceRegexpsChanged() {
|
||||
val isWifiNow = TetherType.ofInterface(downstream).isWifi
|
||||
if (isWifi == isWifiNow) return
|
||||
if (isWifi) WifiDoubleLock.release(this) else WifiDoubleLock.acquire(this)
|
||||
isWifi = isWifiNow
|
||||
}
|
||||
|
||||
private fun initRouting() = try {
|
||||
routing = Routing(caller, downstream, this::ifaceHandler).apply {
|
||||
try {
|
||||
@@ -87,6 +100,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
||||
|
||||
fun stop() {
|
||||
if (active.remove(downstream, this)) {
|
||||
if (!forceWifi && BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this)
|
||||
if (isWifi) WifiDoubleLock.release(this)
|
||||
routing?.revert()
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@ import android.content.Intent
|
||||
import androidx.annotation.RequiresApi
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.net.Routing
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.util.Event0
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class TetheringService : IpNeighbourMonitoringService(), TetheringManager.TetheringEventCallback, CoroutineScope {
|
||||
@@ -31,7 +29,7 @@ class TetheringService : IpNeighbourMonitoringService(), TetheringManager.Tether
|
||||
}
|
||||
|
||||
private inner class Downstream(caller: Any, downstream: String, var monitor: Boolean = false) :
|
||||
RoutingManager(caller, downstream, TetherType.ofInterface(downstream).isWifi) {
|
||||
RoutingManager(caller, downstream) {
|
||||
override fun Routing.configure() {
|
||||
forward()
|
||||
masquerade(masqueradeMode)
|
||||
|
||||
@@ -14,6 +14,7 @@ import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.os.BuildCompat
|
||||
import androidx.databinding.BaseObservable
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
@@ -29,6 +30,7 @@ import be.mygod.vpnhotspot.Empty
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.databinding.FragmentClientsBinding
|
||||
import be.mygod.vpnhotspot.databinding.ListitemClientBinding
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
|
||||
import be.mygod.vpnhotspot.room.AppDatabase
|
||||
@@ -45,7 +47,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.NumberFormat
|
||||
|
||||
class ClientsFragment : Fragment() {
|
||||
class ClientsFragment : Fragment(), TetheringManager.TetheringEventCallback {
|
||||
@Parcelize
|
||||
data class NicknameArg(val mac: Long, val nickname: CharSequence) : Parcelable
|
||||
class NicknameDialogFragment : AlertDialogFragment<NicknameArg, Empty>() {
|
||||
@@ -167,8 +169,10 @@ class ClientsFragment : Fragment() {
|
||||
}
|
||||
|
||||
private inner class ClientAdapter : ListAdapter<Client, ClientViewHolder>(Client) {
|
||||
var size = 0
|
||||
|
||||
override fun submitList(list: MutableList<Client>?) {
|
||||
super.submitList(list)
|
||||
super.submitList(list) { size = list?.size ?: 0 }
|
||||
binding.swipeRefresher.isRefreshing = false
|
||||
}
|
||||
|
||||
@@ -226,6 +230,7 @@ class ClientsFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this)
|
||||
super.onStart()
|
||||
// we just put these two thing together as this is the only place we need to use this event for now
|
||||
TrafficRecorder.foregroundListeners[this] = adapter::updateTraffic
|
||||
@@ -239,5 +244,11 @@ class ClientsFragment : Fragment() {
|
||||
override fun onStop() {
|
||||
TrafficRecorder.foregroundListeners -= this
|
||||
super.onStop()
|
||||
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this)
|
||||
}
|
||||
|
||||
override fun onTetherableInterfaceRegexpsChanged() {
|
||||
// icon might be changed due to TetherType changes
|
||||
adapter.notifyItemRangeChanged(0, adapter.size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,13 @@ import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.util.isNotGone
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
|
||||
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener {
|
||||
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener, TetheringManager.TetheringEventCallback {
|
||||
companion object {
|
||||
const val START_REPEATER = 4
|
||||
const val START_LOCAL_ONLY_HOTSPOT = 1
|
||||
@@ -64,15 +65,36 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
TetherManager.WifiLegacy(this@TetheringFragment)
|
||||
}
|
||||
|
||||
private var enabledIfaces = emptyList<String>()
|
||||
private var listDeferred = CompletableDeferred<List<Manager>>().apply { complete(emptyList()) }
|
||||
private fun updateEnabledTypes() {
|
||||
this@TetheringFragment.enabledTypes = enabledIfaces.map { TetherType.ofInterface(it) }.toSet()
|
||||
}
|
||||
|
||||
suspend fun notifyInterfaceChanged(lastList: List<Manager>? = null) {
|
||||
@Suppress("NAME_SHADOWING") val lastList = lastList ?: listDeferred.await()
|
||||
val first = lastList.indexOfFirst { it is InterfaceManager }
|
||||
if (first >= 0) notifyItemRangeChanged(first, lastList.indexOfLast { it is InterfaceManager } - first + 1)
|
||||
}
|
||||
suspend fun notifyTetherTypeChanged() {
|
||||
updateEnabledTypes()
|
||||
val lastList = listDeferred.await()
|
||||
notifyInterfaceChanged(lastList)
|
||||
val first = lastList.indexOfLast { it !is TetherManager } + 1
|
||||
notifyItemRangeChanged(first, lastList.size - first)
|
||||
}
|
||||
|
||||
fun update(activeIfaces: List<String>, localOnlyIfaces: List<String>, erroredIfaces: List<String>) {
|
||||
val deferred = CompletableDeferred<List<Manager>>()
|
||||
listDeferred = deferred
|
||||
ifaceLookup = try {
|
||||
NetworkInterface.getNetworkInterfaces().asSequence().associateBy { it.name }
|
||||
} catch (e: SocketException) {
|
||||
Timber.d(e)
|
||||
emptyMap()
|
||||
}
|
||||
this@TetheringFragment.enabledTypes =
|
||||
(activeIfaces + localOnlyIfaces).map { TetherType.ofInterface(it) }.toSet()
|
||||
enabledIfaces = activeIfaces + localOnlyIfaces
|
||||
updateEnabledTypes()
|
||||
|
||||
val list = ArrayList<Manager>()
|
||||
if (RepeaterService.supported) list.add(repeaterManager)
|
||||
@@ -94,7 +116,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
list.add(wifiManagerLegacy)
|
||||
wifiManagerLegacy.onTetheringStarted()
|
||||
}
|
||||
submitList(list)
|
||||
submitList(list) { deferred.complete(list) }
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int) = getItem(position).type
|
||||
@@ -230,18 +252,20 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
binder = service as TetheringService.Binder
|
||||
service.routingsChanged[this] = {
|
||||
requireContext().apply {
|
||||
// flush tethered interfaces
|
||||
unregisterReceiver(receiver)
|
||||
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
||||
}
|
||||
lifecycleScope.launchWhenStarted { adapter.notifyInterfaceChanged() }
|
||||
}
|
||||
requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
||||
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this)
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
(binder ?: return).routingsChanged -= this
|
||||
binder = null
|
||||
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this)
|
||||
requireContext().unregisterReceiver(receiver)
|
||||
}
|
||||
|
||||
override fun onTetherableInterfaceRegexpsChanged() {
|
||||
lifecycleScope.launchWhenStarted { adapter.notifyTetherTypeChanged() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.service.quicksettings.Tile
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.BuildCompat
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.TetheringService
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
@@ -25,7 +26,8 @@ import java.io.IOException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
@RequiresApi(24)
|
||||
sealed class TetheringTileService : KillableTileService(), TetheringManager.StartTetheringCallback {
|
||||
sealed class TetheringTileService : KillableTileService(), TetheringManager.StartTetheringCallback,
|
||||
TetheringManager.TetheringEventCallback {
|
||||
protected val tileOff by lazy { Icon.createWithResource(application, icon) }
|
||||
protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) }
|
||||
|
||||
@@ -47,12 +49,15 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
bindService(Intent(this, TetheringService::class.java), this, Context.BIND_AUTO_CREATE)
|
||||
// we need to initialize tethered ASAP for onClick, which is not achievable using registerTetheringEventCallback
|
||||
tethered = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
||||
?.tetheredIfaces
|
||||
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this)
|
||||
updateTile()
|
||||
}
|
||||
|
||||
override fun onStopListening() {
|
||||
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this)
|
||||
unregisterReceiver(receiver)
|
||||
stopAndUnbind(this)
|
||||
super.onStopListening()
|
||||
@@ -60,7 +65,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
binder = service as TetheringService.Binder
|
||||
service.routingsChanged[this] = { updateTile() }
|
||||
service.routingsChanged[this] = this::updateTile
|
||||
super.onServiceConnected(name, service)
|
||||
}
|
||||
|
||||
@@ -127,6 +132,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
|
||||
error?.let { Toast.makeText(this, TetheringManager.tetherErrorMessage(it), Toast.LENGTH_LONG).show() }
|
||||
updateTile()
|
||||
}
|
||||
override fun onTetherableInterfaceRegexpsChanged() = updateTile()
|
||||
|
||||
class Wifi : TetheringTileService() {
|
||||
override val labelString get() = R.string.tethering_manage_wifi
|
||||
|
||||
@@ -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,26 +26,24 @@ 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() }
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
@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")
|
||||
@@ -52,12 +51,25 @@ enum class TetherType {
|
||||
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()) {
|
||||
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
|
||||
|
||||
@@ -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 ->
|
||||
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)
|
||||
null
|
||||
}
|
||||
"onUpstreamChanged" -> {
|
||||
if (args.size > 1) Timber.w("Unexpected args for $name: $args")
|
||||
callback?.onUpstreamChanged(args[0] as Network?)
|
||||
null
|
||||
}
|
||||
"onTetherableInterfaceRegexpsChanged" -> {
|
||||
callback?.onTetherableInterfaceRegexpsChanged()
|
||||
null
|
||||
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?>)
|
||||
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)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user