From 689c97f452d9477dc94a8b9299e982fcb22afa1e Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Tue, 1 Aug 2023 17:39:11 -0400 Subject: [PATCH] fix: auto tunneling failure with rapid network changes Fixes issue where rapid network switching could cause unexpected VPN connections and disconnection. Fixed by changing from real time network VPN triggers to three second interval checks. Attempts to optimize battery drain while VPN connected by switching VPN statistics gathering to 10 second intervals. Closes #11, Closes #12, Closes #10 --- app/build.gradle.kts | 2 +- .../wireguardautotunnel/Constants.kt | 6 + .../WireGuardConnectivityWatcherService.kt | 107 ++++++++---------- .../service/tunnel/WireGuardTunnel.kt | 5 +- 4 files changed, 59 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c323f02..8115e8d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,7 +17,7 @@ android { val versionMajor = 2 val versionMinor = 3 - val versionPatch = 1 + val versionPatch = 2 val versionBuild = 0 defaultConfig { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt new file mode 100644 index 0000000..7bffe0b --- /dev/null +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/Constants.kt @@ -0,0 +1,6 @@ +package com.zaneschepke.wireguardautotunnel + +object Constants { + const val VPN_CONNECTIVITY_CHECK_INTERVAL = 3000L; + const val VPN_STATISTIC_CHECK_INTERVAL = 10000L; +} \ No newline at end of file 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 4e5b792..a205ca6 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 @@ -8,7 +8,10 @@ import android.content.Intent import android.os.Bundle import android.os.PowerManager import android.os.SystemClock +import androidx.compose.runtime.collectAsState +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.wireguard.android.backend.Tunnel +import com.zaneschepke.wireguardautotunnel.Constants import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.repository.Repository import com.zaneschepke.wireguardautotunnel.service.network.MobileDataService @@ -22,6 +25,9 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.single import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -46,15 +52,14 @@ class WireGuardConnectivityWatcherService : ForegroundService() { @Inject lateinit var vpnService : VpnService + private var isWifiConnected = false; + private var isMobileDataConnected = false; + private var currentNetworkSSID = ""; + private lateinit var watcherJob : Job; private lateinit var setting : Settings private lateinit var tunnelId: String - private var connecting = false - private var disconnecting = false - private var isWifiConnected = false - private var isMobileDataConnected = false - private var wakeLock: PowerManager.WakeLock? = null private val tag = this.javaClass.name; @@ -136,6 +141,9 @@ class WireGuardConnectivityWatcherService : ForegroundService() { watchForMobileDataConnectivityChanges() } } + CoroutineScope(watcherJob).launch { + manageVpn() + } } } @@ -149,17 +157,9 @@ class WireGuardConnectivityWatcherService : ForegroundService() { is NetworkStatus.CapabilitiesChanged -> { isMobileDataConnected = true Timber.d("Mobile data capabilities changed") - if(!disconnecting && !connecting) { - if(!isWifiConnected && setting.isTunnelOnMobileDataEnabled - && vpnService.getState() == Tunnel.State.DOWN) - startVPN() - } } is NetworkStatus.Unavailable -> { isMobileDataConnected = false - if(!disconnecting && !connecting) { - if(!isWifiConnected && vpnService.getState() == Tunnel.State.UP) stopVPN() - } Timber.d("Lost mobile data connection") } } @@ -176,61 +176,52 @@ class WireGuardConnectivityWatcherService : ForegroundService() { is NetworkStatus.CapabilitiesChanged -> { Timber.d("Wifi capabilities changed") isWifiConnected = true - if (!connecting && !disconnecting) { - Timber.d("Not connect and not disconnecting") - val ssid = wifiService.getNetworkName(it.networkCapabilities); - Timber.d("SSID: $ssid") - if (!setting.trustedNetworkSSIDs.contains(ssid) && vpnService.getState() == Tunnel.State.DOWN) { - Timber.d("Starting VPN Tunnel for untrusted network: $ssid") - startVPN() - } else if (!disconnecting && vpnService.getState() == Tunnel.State.UP && setting.trustedNetworkSSIDs.contains( - ssid - ) - ) { - Timber.d("Stopping VPN Tunnel for trusted network with ssid: $ssid") - stopVPN() - } - } + currentNetworkSSID = wifiService.getNetworkName(it.networkCapabilities) ?: ""; } is NetworkStatus.Unavailable -> { isWifiConnected = false Timber.d("Lost Wi-Fi connection") - if(!connecting || !disconnecting) { - if(setting.isTunnelOnMobileDataEnabled && vpnService.getState() == Tunnel.State.DOWN - && isMobileDataConnected){ - Timber.d("Wifi not available so starting vpn for mobile data") - startVPN() - } - if(!setting.isTunnelOnMobileDataEnabled && vpnService.getState() == Tunnel.State.UP) { - Timber.d("Lost WiFi connection, disabling vpn") - stopVPN() - } - } - } } } } - private fun startVPN() { - if(!connecting) { - connecting = true - ServiceTracker.actionOnService( - Action.START, - this.applicationContext as Application, - WireGuardTunnelService::class.java, - mapOf(getString(R.string.tunnel_extras_key) to tunnelId)) - connecting = false + + private suspend fun manageVpn() { + while(watcherJob.isActive) { + if(setting.isTunnelOnMobileDataEnabled && + !isWifiConnected && + isMobileDataConnected + && vpnService.getState() == Tunnel.State.DOWN) { + startVPN() + } else if(!setting.isTunnelOnMobileDataEnabled && + !isWifiConnected && + vpnService.getState() == Tunnel.State.UP) { + stopVPN() + } else if(isWifiConnected && + !setting.trustedNetworkSSIDs.contains(currentNetworkSSID) && + (vpnService.getState() != Tunnel.State.UP)) { + startVPN() + } else if((isWifiConnected && + setting.trustedNetworkSSIDs.contains(currentNetworkSSID)) && + (vpnService.getState() == Tunnel.State.UP)) { + stopVPN() + } + delay(Constants.VPN_CONNECTIVITY_CHECK_INTERVAL) } } + + private fun startVPN() { + ServiceTracker.actionOnService( + Action.START, + this.applicationContext as Application, + WireGuardTunnelService::class.java, + mapOf(getString(R.string.tunnel_extras_key) to tunnelId)) + } private fun stopVPN() { - if(!disconnecting) { - disconnecting = true - ServiceTracker.actionOnService( - Action.STOP, - this.applicationContext as Application, - WireGuardTunnelService::class.java - ) - disconnecting = false - } + ServiceTracker.actionOnService( + Action.STOP, + this.applicationContext as Application, + WireGuardTunnelService::class.java + ) } } \ No newline at end of file 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 289af22..a8d5110 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 @@ -5,6 +5,7 @@ import com.wireguard.android.backend.BackendException import com.wireguard.android.backend.Statistics import com.wireguard.android.backend.Tunnel import com.wireguard.crypto.Key +import com.zaneschepke.wireguardautotunnel.Constants import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig import com.zaneschepke.wireguardautotunnel.util.NumberUtils import kotlinx.coroutines.CoroutineScope @@ -103,7 +104,7 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend, _handshakeStatus.emit(HandshakeStatus.NOT_STARTED) } if(neverHadHandshakeCounter <= HandshakeStatus.NEVER_CONNECTED_TO_UNHEALTHY_TIME_LIMIT_SEC) { - neverHadHandshakeCounter++ + neverHadHandshakeCounter += 10 } return@forEach } @@ -114,7 +115,7 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend, } } _lastHandshake.emit(handshakeMap) - delay(1000) + delay(Constants.VPN_STATISTIC_CHECK_INTERVAL) } } }