Use in-process listener to ensure callback order
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user