From eeccc71469e6ac9bfe934a67a3f10c1623db6048 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Fri, 1 Sep 2023 13:05:17 -0400 Subject: [PATCH] fix: tile auto tunnel toggle and wifi internet connection check Change tile toggle behavior to also toggle auto tunnel service to allow user temporary override and re-enablement of auto tunnel from the tile. Add tunnel name to tile. Fix bug where wifi networks without internet access were impacting auto tunneling determinations. Closes #22 --- app/build.gradle.kts | 2 +- .../service/TunnelControlTile.kt | 109 +++++++++++------- .../service/foreground/ForegroundService.kt | 3 - .../service/foreground/ServiceTracker.kt | 2 + .../WireGuardConnectivityWatcherService.kt | 1 - .../service/network/BaseNetworkService.kt | 3 +- .../service/tunnel/WireGuardTunnel.kt | 1 + .../service/tunnel/model/Settings.kt | 2 +- app/src/main/res/values/strings.xml | 2 + 9 files changed, 77 insertions(+), 48 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6ce5264..9c2f84f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,7 +17,7 @@ android { val versionMajor = 2 val versionMinor = 3 - val versionPatch = 6 + val versionPatch = 7 val versionBuild = 0 defaultConfig { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/TunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/TunnelControlTile.kt index 0132a81..d77fdd4 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/TunnelControlTile.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/TunnelControlTile.kt @@ -7,6 +7,7 @@ import com.wireguard.android.backend.Tunnel import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.repository.Repository import com.zaneschepke.wireguardautotunnel.service.foreground.Action +import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService @@ -39,18 +40,15 @@ class TunnelControlTile : TileService() { private lateinit var job : Job override fun onStartListening() { - if (!this::job.isInitialized) { - job = scope.launch { - updateTileState() - } + job = scope.launch { + updateTileState() } - Timber.d("On start listening") super.onStartListening() } override fun onTileAdded() { super.onTileAdded() - qsTile.contentDescription = "Toggle VPN" + qsTile.contentDescription = this.resources.getString(R.string.toggle_vpn) scope.launch { updateTileState(); } @@ -62,43 +60,50 @@ class TunnelControlTile : TileService() { } override fun onClick() { + super.onClick() unlockAndRun { scope.launch { try { - if(vpnService.getState() == Tunnel.State.UP) { - stopTunnel(); - return@launch - } - val settings = settingsRepo.getAll() - if (!settings.isNullOrEmpty()) { - val setting = settings.first() - if (setting.defaultTunnel != null) { - startTunnel(setting.defaultTunnel!!) + val tunnel = determineTileTunnel(); + if(tunnel != null) { + attemptWatcherServiceToggle(tunnel.toString()) + if(vpnService.getState() == Tunnel.State.UP) { + stopTunnelService(); } else { - val config = configRepo.getAll()?.first(); - if(config != null) { - startTunnel(config.toString()); - } + startTunnelService(tunnel.toString()) } } + } catch (e : Exception) { + Timber.e(e.message) } finally { cancel() } } - super.onClick() } } - private fun stopTunnel() { + private suspend fun determineTileTunnel() : TunnelConfig? { + var tunnelConfig : TunnelConfig? = null; + val settings = settingsRepo.getAll() + if (!settings.isNullOrEmpty()) { + val setting = settings.first() + tunnelConfig = if (setting.defaultTunnel != null) { + TunnelConfig.from(setting.defaultTunnel!!); + } else { + val config = configRepo.getAll()?.first(); + config; + } + } + return tunnelConfig; + } + + private fun stopTunnelService() { ServiceTracker.actionOnService( - Action.STOP, this@TunnelControlTile, - WireGuardConnectivityWatcherService::class.java) - ServiceTracker.actionOnService( - Action.STOP, this@TunnelControlTile, + Action.STOP, this.applicationContext, WireGuardTunnelService::class.java) } - private fun startTunnel(tunnelConfig : String) { + private fun startTunnelService(tunnelConfig : String) { ServiceTracker.actionOnService( Action.START, this.applicationContext, WireGuardTunnelService::class.java, @@ -107,42 +112,64 @@ class TunnelControlTile : TileService() { tunnelConfig)) } + private fun startWatcherService(tunnelConfig : String) { + ServiceTracker.actionOnService( + Action.START, this, + WireGuardConnectivityWatcherService::class.java, mapOf(this.resources. + getString(R.string.tunnel_extras_key) to + tunnelConfig)) + } + + private fun stopWatcherService() { + ServiceTracker.actionOnService( + Action.STOP, this, + WireGuardConnectivityWatcherService::class.java) + } + + private fun attemptWatcherServiceToggle(tunnelConfig : String) { + scope.launch { + val settings = settingsRepo.getAll() + if (!settings.isNullOrEmpty()) { + val setting = settings.first() + if(setting.isAutoTunnelEnabled) { + when(ServiceTracker.getServiceState( this@TunnelControlTile, + WireGuardConnectivityWatcherService::class.java,)) { + ServiceState.STARTED -> stopWatcherService() + ServiceState.STOPPED -> startWatcherService(tunnelConfig) + } + } + } + } + } + private suspend fun updateTileState() { vpnService.state.collect { when(it) { Tunnel.State.UP -> { - setTileOn() + qsTile.state = Tile.STATE_ACTIVE } Tunnel.State.DOWN -> { - setTileOff() + qsTile.state = Tile.STATE_INACTIVE; } else -> { qsTile.state = Tile.STATE_UNAVAILABLE } } + val config = determineTileTunnel(); + setTileDescription(config?.name ?: this.resources.getString(R.string.no_tunnel_available)) qsTile.updateTile() } } - private fun setTileOff() { - qsTile.state = Tile.STATE_INACTIVE; + private fun setTileDescription(description : String) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - qsTile.subtitle = "Off" + qsTile.subtitle = description } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - qsTile.stateDescription = "VPN Off"; + qsTile.stateDescription = description; } } - private fun setTileOn() { - qsTile.state = Tile.STATE_ACTIVE; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - qsTile.subtitle = "On" - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - qsTile.stateDescription = "VPN On"; - } - } private fun cancelJob() { if(this::job.isInitialized) { job.cancel(); diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ForegroundService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ForegroundService.kt index 55d94cb..eb94d66 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ForegroundService.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ForegroundService.kt @@ -4,10 +4,7 @@ import android.app.Service import android.content.Intent import android.os.Bundle import android.os.IBinder -import com.zaneschepke.wireguardautotunnel.repository.Repository -import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings import timber.log.Timber -import javax.inject.Inject open class ForegroundService : Service() { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceTracker.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceTracker.kt index 9c885ec..64e5ceb 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceTracker.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceTracker.kt @@ -24,6 +24,7 @@ object ServiceTracker { fun actionOnService(action: Action, application: Application, cls : Class, extras : Map? = null) { if (getServiceState(application, cls) == ServiceState.STOPPED && action == Action.STOP) return + if (getServiceState(application, cls) == ServiceState.STARTED && action == Action.START) return val intent = Intent(application, cls).also { it.action = action.name extras?.forEach {(k, v) -> @@ -40,6 +41,7 @@ object ServiceTracker { fun actionOnService(action: Action, context: Context, cls : Class, extras : Map? = null) { if (getServiceState(context, cls) == ServiceState.STOPPED && action == Action.STOP) return + if (getServiceState(context, cls) == ServiceState.STARTED && action == Action.START) return val intent = Intent(context, cls).also { it.action = action.name extras?.forEach {(k, v) -> diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt index 821ba98..30354b2 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardConnectivityWatcherService.kt @@ -85,7 +85,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() { } } cancelWatcherJob() - stopVPN() stopSelf() } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/network/BaseNetworkService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/network/BaseNetworkService.kt index 312617a..b9e5e8d 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/network/BaseNetworkService.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/network/BaseNetworkService.kt @@ -28,7 +28,6 @@ abstract class BaseNetworkService>(val context: Contex object : ConnectivityManager.NetworkCallback( FLAG_INCLUDE_LOCATION_INFO ) { - override fun onAvailable(network: Network) { trySend(NetworkStatus.Available(network)) } @@ -68,6 +67,8 @@ abstract class BaseNetworkService>(val context: Contex } val request = NetworkRequest.Builder() .addTransportType(networkCapability) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) .build() connectivityManager.registerNetworkCallback(request, networkStatusCallback) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt index a8d5110..2f40c71 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/WireGuardTunnel.kt @@ -30,6 +30,7 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend, override val tunnelName get() = _tunnelName.asStateFlow() private val _state = MutableSharedFlow( + onBufferOverflow = BufferOverflow.DROP_OLDEST, replay = 1) private val _handshakeStatus = MutableSharedFlow(replay = 1, diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/model/Settings.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/model/Settings.kt index 742baa2..a868467 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/model/Settings.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tunnel/model/Settings.kt @@ -11,5 +11,5 @@ data class Settings( var isTunnelOnMobileDataEnabled : Boolean = false, var trustedNetworkSSIDs : MutableList = mutableListOf(), var defaultTunnel : String? = null, - var isAlwaysOnVpnEnabled : Boolean = false + var isAlwaysOnVpnEnabled : Boolean = false, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a06e840..b84d101 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -84,4 +84,6 @@ Detecting Location Services disabled This feature requires precise location to access Wi-Fi SSID name. Please enable precise location here or in the app settings. Request + Toggle VPN + No tunnels available \ No newline at end of file