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 versionMinor = 1
val versionPatch = 3
val versionPatch = 4
val versionBuild = 0
defaultConfig {

View File

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

View File

@ -1,29 +1,25 @@
package com.zaneschepke.wireguardautotunnel.service.foreground
import android.app.ActivityManager
import android.app.Application
import android.app.Service
import android.content.Context
import android.content.Context.ACTIVITY_SERVICE
import android.content.Intent
import android.content.SharedPreferences
import com.zaneschepke.wireguardautotunnel.R
object ServiceTracker {
fun <T : Service> setServiceState(context: Context, state: ServiceState, cls : Class<T>) {
val sharedPrefs = getPreferences(context)
sharedPrefs.edit().let {
it.putString(cls.simpleName, state.name)
it.apply()
}
}
@Suppress("DEPRECATION")
private // Deprecated for third party Services.
fun <T> Context.isServiceRunning(service: Class<T>) =
(getSystemService(ACTIVITY_SERVICE) as ActivityManager)
.getRunningServices(Integer.MAX_VALUE)
.any { it.service.className == service.name }
private fun <T : Service> getServiceState(context: Context, cls : Class<T>): ServiceState {
val sharedPrefs = getPreferences(context)
val value = sharedPrefs.getString(cls.simpleName, ServiceState.STOPPED.name)
return ServiceState.valueOf(value!!)
}
private fun getPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(context.resources.getString(R.string.foreground_file), 0)
fun <T : Service> getServiceState(context: Context, cls : Class<T>): ServiceState {
val isServiceRunning = context.isServiceRunning(cls)
return if(isServiceRunning) ServiceState.STARTED else ServiceState.STOPPED
}
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()) {
setting = settings[0]
}
watchForWifiConnectivityChanges()
if(setting.isTunnelOnMobileDataEnabled) {
GlobalScope.launch {
watchForMobileDataConnectivityChanges()
}
}
watchForWifiConnectivityChanges()
}
}
@ -171,16 +171,18 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
isWifiConnected = true
}
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) == false) && 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(
} else if (!disconnecting && vpnService.getState() == Tunnel.State.UP && setting.trustedNetworkSSIDs.contains(
ssid
) == true)
)
) {
Timber.d("Stopping VPN Tunnel for trusted network with ssid: $ssid")
stopVPN()
@ -191,7 +193,10 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
isWifiConnected = false
Timber.d("Lost Wi-Fi connection")
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.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
@ -23,6 +24,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@ -48,11 +50,26 @@ class MainViewModel @Inject constructor(private val application : Application,
viewModelScope.launch {
settingsRepo.itemFlow.collect {
val settings = it.first()
validateWatcherServiceState(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) {
viewModelScope.launch {
if(tunnelRepo.count() == 1L) {