From 4447dfd86e84425bc5dffa8838900fb366318177 Mon Sep 17 00:00:00 2001 From: Mygod Date: Thu, 28 May 2020 15:08:47 -0400 Subject: [PATCH] Use TetheringEventCallback when appropriate --- .../be/mygod/vpnhotspot/TetheringService.kt | 29 +++++++-------- .../mygod/vpnhotspot/net/TetheringManager.kt | 35 +++++++++++++++++-- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt index 426bb53a..49d3cba0 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/TetheringService.kt @@ -1,20 +1,16 @@ package be.mygod.vpnhotspot import android.content.Intent -import android.content.IntentFilter 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.TetheringManager.tetheredIfaces import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.util.Event0 -import be.mygod.vpnhotspot.util.broadcastReceiver -import be.mygod.vpnhotspot.util.ensureReceiverUnregistered import kotlinx.coroutines.* import java.util.concurrent.ConcurrentHashMap -class TetheringService : IpNeighbourMonitoringService(), CoroutineScope { +class TetheringService : IpNeighbourMonitoringService(), TetheringManager.TetheringEventCallback, CoroutineScope { companion object { const val EXTRA_ADD_INTERFACES = "interface.add" const val EXTRA_ADD_INTERFACE_MONITOR = "interface.add.monitor" @@ -47,11 +43,14 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope { override val coroutineContext = dispatcher + Job() private val binder = Binder() private val downstreams = ConcurrentHashMap() - private var receiverRegistered = false - private val receiver = broadcastReceiver { _, intent -> + private var callbackRegistered = false + 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) { launch { val toRemove = downstreams.toMutableMap() // make a copy - for (iface in intent.tetheredIfaces ?: return@launch) { + for (iface in interfaces) { val downstream = toRemove.remove(iface) ?: continue if (downstream.monitor) downstream.start() } @@ -62,8 +61,6 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope { 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() { if (downstreams.isEmpty()) { @@ -71,9 +68,9 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope { ServiceNotification.stopForeground(this) stopSelf() } else { - if (!receiverRegistered) { - receiverRegistered = true - registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) + if (!callbackRegistered) { + callbackRegistered = true + TetheringManager.registerTetheringEventCallbackCompat(this, this) IpNeighbourMonitor.registerCallback(this) } updateNotification() @@ -118,10 +115,10 @@ class TetheringService : IpNeighbourMonitoringService(), CoroutineScope { } private fun unregisterReceiver() { - if (receiverRegistered) { - ensureReceiverUnregistered(receiver) + if (callbackRegistered) { + TetheringManager.unregisterTetheringEventCallbackCompat(this, this) IpNeighbourMonitor.unregisterCallback(this) - receiverRegistered = false + callbackRegistered = false } } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt index 2a86d365..4938d4f9 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/TetheringManager.kt @@ -1,6 +1,9 @@ package be.mygod.vpnhotspot.net +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageManager import android.net.ConnectivityManager import android.net.LinkAddress @@ -12,6 +15,8 @@ import androidx.collection.SparseArrayCompat import androidx.core.os.BuildCompat import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.R +import be.mygod.vpnhotspot.util.broadcastReceiver +import be.mygod.vpnhotspot.util.ensureReceiverUnregistered import com.android.dx.stock.ProxyBuilder import timber.log.Timber import java.lang.ref.WeakReference @@ -185,6 +190,8 @@ object TetheringManager { @get:RequiresApi(30) 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 * the check succeeds. If no carrier provisioning is required for tethering, tethering is @@ -228,7 +235,6 @@ object TetheringManager { } build.invoke(this) } - val executor = Executor { if (handler == null) it.run() else handler.post(it) } val proxy = Proxy.newProxyInstance(interfaceStartTetheringCallback.classLoader, arrayOf(interfaceStartTetheringCallback)) { proxy, method, args -> @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 } catch (e: InvocationTargetException) { Timber.w(e, "Unable to invoke TetheringManager.startTethering, falling back to ConnectivityManager") @@ -492,6 +498,31 @@ object TetheringManager { 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 * request asynchronously failed.