fix: service status out of sync

This fixes the status of service becoming out of sync with the user settings by getting service status from ActivityManager.
This commit is contained in:
Zane Schepke 2023-06-30 21:54:04 -04:00
parent f894a3e0c9
commit 98da234ef9
5 changed files with 38 additions and 22 deletions

View File

@ -17,7 +17,7 @@ android {
val versionMajor = 1 val versionMajor = 1
val versionMinor = 1 val versionMinor = 1
val versionPatch = 3 val versionPatch = 4
val versionBuild = 0 val versionBuild = 0
defaultConfig { defaultConfig {

View File

@ -45,7 +45,6 @@ open class ForegroundService : Service() {
if (isServiceStarted) return if (isServiceStarted) return
Timber.d("Starting ${this.javaClass.simpleName}") Timber.d("Starting ${this.javaClass.simpleName}")
isServiceStarted = true isServiceStarted = true
ServiceTracker.setServiceState(this, ServiceState.STARTED, this.javaClass)
} }
protected open fun stopService(extras : Bundle?) { protected open fun stopService(extras : Bundle?) {
@ -57,6 +56,5 @@ open class ForegroundService : Service() {
Timber.d("Service stopped without being started: ${e.message}") Timber.d("Service stopped without being started: ${e.message}")
} }
isServiceStarted = false isServiceStarted = false
ServiceTracker.setServiceState(this, ServiceState.STOPPED, this.javaClass)
} }
} }

View File

@ -1,29 +1,25 @@
package com.zaneschepke.wireguardautotunnel.service.foreground package com.zaneschepke.wireguardautotunnel.service.foreground
import android.app.ActivityManager
import android.app.Application import android.app.Application
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Context.ACTIVITY_SERVICE
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
object ServiceTracker { object ServiceTracker {
fun <T : Service> setServiceState(context: Context, state: ServiceState, cls : Class<T>) { @Suppress("DEPRECATION")
val sharedPrefs = getPreferences(context) private // Deprecated for third party Services.
sharedPrefs.edit().let { fun <T> Context.isServiceRunning(service: Class<T>) =
it.putString(cls.simpleName, state.name) (getSystemService(ACTIVITY_SERVICE) as ActivityManager)
it.apply() .getRunningServices(Integer.MAX_VALUE)
} .any { it.service.className == service.name }
}
private fun <T : Service> getServiceState(context: Context, cls : Class<T>): ServiceState { fun <T : Service> getServiceState(context: Context, cls : Class<T>): ServiceState {
val sharedPrefs = getPreferences(context) val isServiceRunning = context.isServiceRunning(cls)
val value = sharedPrefs.getString(cls.simpleName, ServiceState.STOPPED.name) return if(isServiceRunning) ServiceState.STARTED else ServiceState.STOPPED
return ServiceState.valueOf(value!!)
}
private fun getPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(context.resources.getString(R.string.foreground_file), 0)
} }
fun <T : Service> actionOnService(action: Action, application: Application, cls : Class<T>, extras : Map<String,String>? = null) { fun <T : Service> actionOnService(action: Action, application: Application, cls : Class<T>, extras : Map<String,String>? = null) {

View File

@ -131,12 +131,12 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
if(!settings.isNullOrEmpty()) { if(!settings.isNullOrEmpty()) {
setting = settings[0] setting = settings[0]
} }
watchForWifiConnectivityChanges()
if(setting.isTunnelOnMobileDataEnabled) { if(setting.isTunnelOnMobileDataEnabled) {
GlobalScope.launch { GlobalScope.launch {
watchForMobileDataConnectivityChanges() watchForMobileDataConnectivityChanges()
} }
} }
watchForWifiConnectivityChanges()
} }
} }
@ -171,16 +171,18 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
isWifiConnected = true isWifiConnected = true
} }
is NetworkStatus.CapabilitiesChanged -> { is NetworkStatus.CapabilitiesChanged -> {
Timber.d("Wifi capabilities changed")
isWifiConnected = true isWifiConnected = true
if (!connecting && !disconnecting) { if (!connecting && !disconnecting) {
Timber.d("Not connect and not disconnecting")
val ssid = wifiService.getNetworkName(it.networkCapabilities); val ssid = wifiService.getNetworkName(it.networkCapabilities);
Timber.d("SSID: $ssid") Timber.d("SSID: $ssid")
if ((setting.trustedNetworkSSIDs?.contains(ssid) == false) && vpnService.getState() == Tunnel.State.DOWN) { if ((setting.trustedNetworkSSIDs?.contains(ssid) == false) && vpnService.getState() == Tunnel.State.DOWN) {
Timber.d("Starting VPN Tunnel for untrusted network: $ssid") Timber.d("Starting VPN Tunnel for untrusted network: $ssid")
startVPN() startVPN()
} else if (!disconnecting && vpnService.getState() == Tunnel.State.UP && (setting.trustedNetworkSSIDs?.contains( } else if (!disconnecting && vpnService.getState() == Tunnel.State.UP && setting.trustedNetworkSSIDs.contains(
ssid ssid
) == true) )
) { ) {
Timber.d("Stopping VPN Tunnel for trusted network with ssid: $ssid") Timber.d("Stopping VPN Tunnel for trusted network with ssid: $ssid")
stopVPN() stopVPN()
@ -191,7 +193,10 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
isWifiConnected = false isWifiConnected = false
Timber.d("Lost Wi-Fi connection") Timber.d("Lost Wi-Fi connection")
if(setting.isTunnelOnMobileDataEnabled && vpnService.getState() == Tunnel.State.DOWN if(setting.isTunnelOnMobileDataEnabled && vpnService.getState() == Tunnel.State.DOWN
&& isMobileDataConnected) startVPN() && isMobileDataConnected){
Timber.d("Wifi not available so starting vpn for mobile data")
startVPN()
}
} }
} }
} }

View File

@ -11,6 +11,7 @@ import com.wireguard.config.Config
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.repository.Repository import com.zaneschepke.wireguardautotunnel.repository.Repository
import com.zaneschepke.wireguardautotunnel.service.foreground.Action 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.ServiceTracker
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
@ -23,6 +24,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -48,11 +50,26 @@ class MainViewModel @Inject constructor(private val application : Application,
viewModelScope.launch { viewModelScope.launch {
settingsRepo.itemFlow.collect { settingsRepo.itemFlow.collect {
val settings = it.first() val settings = it.first()
validateWatcherServiceState(settings)
_settings.emit(settings) _settings.emit(settings)
} }
} }
} }
private fun validateWatcherServiceState(settings: Settings) {
val watcherState = ServiceTracker.getServiceState(application, WireGuardConnectivityWatcherService::class.java)
if(settings.isAutoTunnelEnabled && watcherState == ServiceState.STOPPED && settings.defaultTunnel != null) {
startWatcherService(settings.defaultTunnel!!)
}
}
private fun startWatcherService(tunnel : String) {
ServiceTracker.actionOnService(
Action.START, application,
WireGuardConnectivityWatcherService::class.java,
mapOf(application.resources.getString(R.string.tunnel_extras_key) to tunnel))
}
fun onDelete(tunnel : TunnelConfig) { fun onDelete(tunnel : TunnelConfig) {
viewModelScope.launch { viewModelScope.launch {
if(tunnelRepo.count() == 1L) { if(tunnelRepo.count() == 1L) {