Use TetheringEventCallback when appropriate

This commit is contained in:
Mygod
2020-05-28 15:08:47 -04:00
parent cd7e55c992
commit 4447dfd86e
2 changed files with 46 additions and 18 deletions

View File

@@ -1,20 +1,16 @@
package be.mygod.vpnhotspot package be.mygod.vpnhotspot
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.Routing
import be.mygod.vpnhotspot.net.TetherType import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.util.Event0 import be.mygod.vpnhotspot.util.Event0
import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.ensureReceiverUnregistered
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class TetheringService : IpNeighbourMonitoringService(), CoroutineScope { class TetheringService : IpNeighbourMonitoringService(), TetheringManager.TetheringEventCallback, CoroutineScope {
companion object { companion object {
const val EXTRA_ADD_INTERFACES = "interface.add" const val EXTRA_ADD_INTERFACES = "interface.add"
const val EXTRA_ADD_INTERFACE_MONITOR = "interface.add.monitor" const val EXTRA_ADD_INTERFACE_MONITOR = "interface.add.monitor"
@@ -47,11 +43,14 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope {
override val coroutineContext = dispatcher + Job() override val coroutineContext = dispatcher + Job()
private val binder = Binder() private val binder = Binder()
private val downstreams = ConcurrentHashMap<String, Downstream>() private val downstreams = ConcurrentHashMap<String, Downstream>()
private var receiverRegistered = false private var callbackRegistered = false
private val receiver = broadcastReceiver { _, intent -> override val activeIfaces get() = downstreams.values.filter { it.started }.map { it.downstream }
override val inactiveIfaces get() = downstreams.values.filter { !it.started }.map { it.downstream }
override fun onTetheredInterfacesChanged(interfaces: List<String?>) {
launch { launch {
val toRemove = downstreams.toMutableMap() // make a copy val toRemove = downstreams.toMutableMap() // make a copy
for (iface in intent.tetheredIfaces ?: return@launch) { for (iface in interfaces) {
val downstream = toRemove.remove(iface) ?: continue val downstream = toRemove.remove(iface) ?: continue
if (downstream.monitor) downstream.start() if (downstream.monitor) downstream.start()
} }
@@ -62,8 +61,6 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope {
onDownstreamsChangedLocked() onDownstreamsChangedLocked()
} }
} }
override val activeIfaces get() = downstreams.values.filter { it.started }.map { it.downstream }
override val inactiveIfaces get() = downstreams.values.filter { !it.started }.map { it.downstream }
private fun onDownstreamsChangedLocked() { private fun onDownstreamsChangedLocked() {
if (downstreams.isEmpty()) { if (downstreams.isEmpty()) {
@@ -71,9 +68,9 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope {
ServiceNotification.stopForeground(this) ServiceNotification.stopForeground(this)
stopSelf() stopSelf()
} else { } else {
if (!receiverRegistered) { if (!callbackRegistered) {
receiverRegistered = true callbackRegistered = true
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) TetheringManager.registerTetheringEventCallbackCompat(this, this)
IpNeighbourMonitor.registerCallback(this) IpNeighbourMonitor.registerCallback(this)
} }
updateNotification() updateNotification()
@@ -118,10 +115,10 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope {
} }
private fun unregisterReceiver() { private fun unregisterReceiver() {
if (receiverRegistered) { if (callbackRegistered) {
ensureReceiverUnregistered(receiver) TetheringManager.unregisterTetheringEventCallbackCompat(this, this)
IpNeighbourMonitor.unregisterCallback(this) IpNeighbourMonitor.unregisterCallback(this)
receiverRegistered = false callbackRegistered = false
} }
} }
} }

View File

@@ -1,6 +1,9 @@
package be.mygod.vpnhotspot.net package be.mygod.vpnhotspot.net
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.LinkAddress import android.net.LinkAddress
@@ -12,6 +15,8 @@ import androidx.collection.SparseArrayCompat
import androidx.core.os.BuildCompat import androidx.core.os.BuildCompat
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.ensureReceiverUnregistered
import com.android.dx.stock.ProxyBuilder import com.android.dx.stock.ProxyBuilder
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@@ -185,6 +190,8 @@ object TetheringManager {
@get:RequiresApi(30) @get:RequiresApi(30)
private val stopTethering by lazy { clazz.getDeclaredMethod("stopTethering", Int::class.java) } private val stopTethering by lazy { clazz.getDeclaredMethod("stopTethering", Int::class.java) }
private fun Handler?.makeExecutor() = Executor { if (this == null) it.run() else post(it) }
/** /**
* Runs tether provisioning for the given type if needed and then starts tethering if * Runs tether provisioning for the given type if needed and then starts tethering if
* the check succeeds. If no carrier provisioning is required for tethering, tethering is * the check succeeds. If no carrier provisioning is required for tethering, tethering is
@@ -228,7 +235,6 @@ object TetheringManager {
} }
build.invoke(this) build.invoke(this)
} }
val executor = Executor { if (handler == null) it.run() else handler.post(it) }
val proxy = Proxy.newProxyInstance(interfaceStartTetheringCallback.classLoader, val proxy = Proxy.newProxyInstance(interfaceStartTetheringCallback.classLoader,
arrayOf(interfaceStartTetheringCallback)) { proxy, method, args -> arrayOf(interfaceStartTetheringCallback)) { proxy, method, args ->
@Suppress("NAME_SHADOWING") val callback = reference.get() @Suppress("NAME_SHADOWING") val callback = reference.get()
@@ -249,7 +255,7 @@ object TetheringManager {
} }
} }
} }
startTethering.invoke(instance, request, executor, proxy) startTethering.invoke(instance, request, handler.makeExecutor(), proxy)
return return
} catch (e: InvocationTargetException) { } catch (e: InvocationTargetException) {
Timber.w(e, "Unable to invoke TetheringManager.startTethering, falling back to ConnectivityManager") Timber.w(e, "Unable to invoke TetheringManager.startTethering, falling back to ConnectivityManager")
@@ -492,6 +498,31 @@ object TetheringManager {
unregisterTetheringEventCallback.invoke(instance, proxy) unregisterTetheringEventCallback.invoke(instance, proxy)
} }
/**
* [registerTetheringEventCallback] in a backwards compatible way.
*
* Only [TetheringEventCallback.onTetheredInterfacesChanged] is supported on API 29-.
*/
fun registerTetheringEventCallbackCompat(context: Context, callback: TetheringEventCallback) {
if (BuildCompat.isAtLeastR()) {
registerTetheringEventCallback(null.makeExecutor(), callback)
} else synchronized(callbackMap) {
callbackMap.computeIfAbsent(callback) {
broadcastReceiver { _, intent ->
callback.onTetheredInterfacesChanged(intent.tetheredIfaces ?: return@broadcastReceiver)
}.also { context.registerReceiver(it, IntentFilter(ACTION_TETHER_STATE_CHANGED)) }
}
}
}
fun unregisterTetheringEventCallbackCompat(context: Context, callback: TetheringEventCallback) {
if (BuildCompat.isAtLeastR()) {
unregisterTetheringEventCallback(callback)
} else {
val receiver = synchronized(callbackMap) { callbackMap.remove(callback) } ?: return
context.ensureReceiverUnregistered(receiver as BroadcastReceiver)
}
}
/** /**
* Get a more detailed error code after a Tethering or Untethering * Get a more detailed error code after a Tethering or Untethering
* request asynchronously failed. * request asynchronously failed.