Use TetheringEventCallback when appropriate
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user