Use in-process listener to ensure callback order

This commit is contained in:
Mygod
2020-05-29 21:33:09 -04:00
parent 069b32a7d9
commit a40a07b76e
5 changed files with 27 additions and 33 deletions

View File

@@ -6,14 +6,12 @@ import androidx.core.os.BuildCompat
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.wifi.WifiDoubleLock import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import timber.log.Timber import timber.log.Timber
import java.net.NetworkInterface import java.net.NetworkInterface
abstract class RoutingManager(private val caller: Any, val downstream: String, abstract class RoutingManager(private val caller: Any, val downstream: String, private val forceWifi: Boolean = false) {
private val forceWifi: Boolean = false) : TetheringManager.TetheringEventCallback {
companion object { companion object {
private const val KEY_MASQUERADE_MODE = "service.masqueradeMode" private const val KEY_MASQUERADE_MODE = "service.masqueradeMode"
var masqueradeMode: Routing.MasqueradeMode var masqueradeMode: Routing.MasqueradeMode
@@ -63,7 +61,13 @@ abstract class RoutingManager(private val caller: Any, val downstream: String,
fun start() = when (val other = active.putIfAbsent(downstream, this)) { fun start() = when (val other = active.putIfAbsent(downstream, this)) {
null -> { null -> {
if (isWifi) WifiDoubleLock.acquire(this) if (isWifi) WifiDoubleLock.acquire(this)
if (!forceWifi && BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this) if (!forceWifi && BuildCompat.isAtLeastR()) TetherType.listener[this] = {
val isWifiNow = TetherType.ofInterface(downstream).isWifi
if (isWifi != isWifiNow) {
if (isWifi) WifiDoubleLock.release(this) else WifiDoubleLock.acquire(this)
isWifi = isWifiNow
}
}
initRouting() initRouting()
} }
this -> true // already started this -> true // already started
@@ -72,13 +76,6 @@ abstract class RoutingManager(private val caller: Any, val downstream: String,
open fun ifaceHandler(iface: NetworkInterface) { } 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 { private fun initRouting() = try {
routing = Routing(caller, downstream, this::ifaceHandler).apply { routing = Routing(caller, downstream, this::ifaceHandler).apply {
try { try {
@@ -100,7 +97,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String,
fun stop() { fun stop() {
if (active.remove(downstream, this)) { if (active.remove(downstream, this)) {
if (!forceWifi && BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this) if (!forceWifi && BuildCompat.isAtLeastR()) TetherType.listener -= this
if (isWifi) WifiDoubleLock.release(this) if (isWifi) WifiDoubleLock.release(this)
routing?.revert() routing?.revert()
} }

View File

@@ -30,7 +30,7 @@ import be.mygod.vpnhotspot.Empty
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.databinding.FragmentClientsBinding import be.mygod.vpnhotspot.databinding.FragmentClientsBinding
import be.mygod.vpnhotspot.databinding.ListitemClientBinding import be.mygod.vpnhotspot.databinding.ListitemClientBinding
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.net.monitor.TrafficRecorder import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.room.AppDatabase
@@ -47,7 +47,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.text.NumberFormat import java.text.NumberFormat
class ClientsFragment : Fragment(), TetheringManager.TetheringEventCallback { class ClientsFragment : Fragment() {
@Parcelize @Parcelize
data class NicknameArg(val mac: Long, val nickname: CharSequence) : Parcelable data class NicknameArg(val mac: Long, val nickname: CharSequence) : Parcelable
class NicknameDialogFragment : AlertDialogFragment<NicknameArg, Empty>() { class NicknameDialogFragment : AlertDialogFragment<NicknameArg, Empty>() {
@@ -230,7 +230,8 @@ class ClientsFragment : Fragment(), TetheringManager.TetheringEventCallback {
} }
override fun onStart() { override fun onStart() {
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this) // icon might be changed due to TetherType changes
if (BuildCompat.isAtLeastR()) TetherType.listener[this] = { adapter.notifyItemRangeChanged(0, adapter.size) }
super.onStart() super.onStart()
// we just put these two thing together as this is the only place we need to use this event for now // 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 TrafficRecorder.foregroundListeners[this] = adapter::updateTraffic
@@ -244,11 +245,6 @@ class ClientsFragment : Fragment(), TetheringManager.TetheringEventCallback {
override fun onStop() { override fun onStop() {
TrafficRecorder.foregroundListeners -= this TrafficRecorder.foregroundListeners -= this
super.onStop() super.onStop()
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this) if (BuildCompat.isAtLeastR()) TetherType.listener -= this
}
override fun onTetherableInterfaceRegexpsChanged() {
// icon might be changed due to TetherType changes
adapter.notifyItemRangeChanged(0, adapter.size)
} }
} }

View File

@@ -38,7 +38,7 @@ import java.lang.reflect.InvocationTargetException
import java.net.NetworkInterface import java.net.NetworkInterface
import java.net.SocketException import java.net.SocketException
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener, TetheringManager.TetheringEventCallback { class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener {
companion object { companion object {
const val START_REPEATER = 4 const val START_REPEATER = 4
const val START_LOCAL_ONLY_HOTSPOT = 1 const val START_LOCAL_ONLY_HOTSPOT = 1
@@ -255,17 +255,15 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
lifecycleScope.launchWhenStarted { adapter.notifyInterfaceChanged() } lifecycleScope.launchWhenStarted { adapter.notifyInterfaceChanged() }
} }
requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this) if (BuildCompat.isAtLeastR()) TetherType.listener[this] = {
lifecycleScope.launchWhenStarted { adapter.notifyTetherTypeChanged() }
}
} }
override fun onServiceDisconnected(name: ComponentName?) { override fun onServiceDisconnected(name: ComponentName?) {
(binder ?: return).routingsChanged -= this (binder ?: return).routingsChanged -= this
binder = null binder = null
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this) if (BuildCompat.isAtLeastR()) TetherType.listener -= this
requireContext().unregisterReceiver(receiver) requireContext().unregisterReceiver(receiver)
} }
override fun onTetherableInterfaceRegexpsChanged() {
lifecycleScope.launchWhenStarted { adapter.notifyTetherTypeChanged() }
}
} }

View File

@@ -26,8 +26,7 @@ import java.io.IOException
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@RequiresApi(24) @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 tileOff by lazy { Icon.createWithResource(application, icon) }
protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) } protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) }
@@ -52,12 +51,12 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
// we need to initialize tethered ASAP for onClick, which is not achievable using registerTetheringEventCallback // we need to initialize tethered ASAP for onClick, which is not achievable using registerTetheringEventCallback
tethered = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)) tethered = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
?.tetheredIfaces ?.tetheredIfaces
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this) if (BuildCompat.isAtLeastR()) TetherType.listener[this] = this::updateTile
updateTile() updateTile()
} }
override fun onStopListening() { override fun onStopListening() {
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this) if (BuildCompat.isAtLeastR()) TetherType.listener -= this
unregisterReceiver(receiver) unregisterReceiver(receiver)
stopAndUnbind(this) stopAndUnbind(this)
super.onStopListening() super.onStopListening()
@@ -132,7 +131,6 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
error?.let { Toast.makeText(this, TetheringManager.tetherErrorMessage(it), Toast.LENGTH_LONG).show() } error?.let { Toast.makeText(this, TetheringManager.tetherErrorMessage(it), Toast.LENGTH_LONG).show() }
updateTile() updateTile()
} }
override fun onTetherableInterfaceRegexpsChanged() = updateTile()
class Wifi : TetheringTileService() { class Wifi : TetheringTileService() {
override val labelString get() = R.string.tethering_manage_wifi override val labelString get() = R.string.tethering_manage_wifi

View File

@@ -5,6 +5,7 @@ import androidx.annotation.RequiresApi
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.Event0
import java.util.regex.Pattern import java.util.regex.Pattern
enum class TetherType { enum class TetherType {
@@ -36,6 +37,9 @@ enum class TetherType {
private val ethernetRegex: Pattern? private val ethernetRegex: Pattern?
private var requiresUpdate = true private var requiresUpdate = true
@RequiresApi(30) // unused on lower APIs
val listener = Event0()
private fun Pair<String?, Resources>.getRegexs(name: String) = second private fun Pair<String?, Resources>.getRegexs(name: String) = second
.getStringArray(second.getIdentifier(name, "array", first)) .getStringArray(second.getIdentifier(name, "array", first))
.filterNotNull() .filterNotNull()
@@ -56,6 +60,7 @@ enum class TetherType {
@RequiresApi(30) @RequiresApi(30)
override fun onTetherableInterfaceRegexpsChanged() { override fun onTetherableInterfaceRegexpsChanged() {
requiresUpdate = true requiresUpdate = true
listener()
} }
/** /**