From 1c0d968cfbe602bf017d429d535290d94dc3582d Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Sat, 5 Apr 2025 20:17:16 -0400 Subject: [PATCH] fix: auto tunnel tile state bug --- .../core/broadcast/RestartReceiver.kt | 4 +- .../core/service/ServiceManager.kt | 73 +++++++++++-------- .../service/tile/AutoTunnelControlTile.kt | 7 +- .../core/service/tile/TunnelControlTile.kt | 4 +- .../wireguardautotunnel/di/TunnelModule.kt | 3 +- 5 files changed, 51 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/RestartReceiver.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/RestartReceiver.kt index 8dcb30a8..784df123 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/RestartReceiver.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/broadcast/RestartReceiver.kt @@ -44,9 +44,9 @@ class RestartReceiver : BroadcastReceiver() { } Timber.d("RestartReceiver triggered with action: ${intent.action}") + serviceManager.updateTunnelTile() + serviceManager.updateAutoTunnelTile() applicationScope.launch(ioDispatcher) { - serviceManager.updateTunnelTile() - serviceManager.updateAutoTunnelTile() val settings = appDataRepository.settings.get() if (settings.isRestoreOnBootEnabled) { if (settings.isAutoTunnelEnabled && !serviceManager.autoTunnelActive.value) { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/ServiceManager.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/ServiceManager.kt index aa8050d2..b346b83a 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/ServiceManager.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/ServiceManager.kt @@ -8,6 +8,7 @@ import com.zaneschepke.wireguardautotunnel.WireGuardAutoTunnel import com.zaneschepke.wireguardautotunnel.core.service.autotunnel.AutoTunnelService import com.zaneschepke.wireguardautotunnel.di.ApplicationScope import com.zaneschepke.wireguardautotunnel.di.IoDispatcher +import com.zaneschepke.wireguardautotunnel.di.MainDispatcher import com.zaneschepke.wireguardautotunnel.domain.entity.TunnelConf import com.zaneschepke.wireguardautotunnel.domain.repository.AppDataRepository import com.zaneschepke.wireguardautotunnel.util.extensions.requestAutoTunnelTileServiceUpdate @@ -20,6 +21,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber @@ -27,9 +31,12 @@ class ServiceManager @Inject constructor( private val context: Context, @IoDispatcher private val ioDispatcher: CoroutineDispatcher, @ApplicationScope private val applicationScope: CoroutineScope, + @MainDispatcher private val mainDispatcher: CoroutineDispatcher, private val appDataRepository: AppDataRepository, ) { + private val autoTunnelMutex = Mutex() + private val _autoTunnelActive = MutableStateFlow(false) val autoTunnelActive = _autoTunnelActive.asStateFlow() @@ -53,22 +60,43 @@ class ServiceManager @Inject constructor( fun startAutoTunnel() { applicationScope.launch(ioDispatcher) { - val settings = appDataRepository.settings.get() - appDataRepository.settings.save(settings.copy(isAutoTunnelEnabled = true)) - if (autoTunnelService.isCompleted) { - _autoTunnelActive.update { true } - return@launch - } - runCatching { - autoTunnelService = CompletableDeferred() - startService(AutoTunnelService::class.java, !WireGuardAutoTunnel.isForeground()) - _autoTunnelActive.update { true } - }.onFailure { - Timber.e(it) - _autoTunnelActive.update { false } + autoTunnelMutex.withLock { + val settings = appDataRepository.settings.get() + appDataRepository.settings.save(settings.copy(isAutoTunnelEnabled = true)) + if (autoTunnelService.isCompleted) { + _autoTunnelActive.update { true } + return@launch + } + runCatching { + autoTunnelService = CompletableDeferred() + startService(AutoTunnelService::class.java, !WireGuardAutoTunnel.isForeground()) + _autoTunnelActive.update { true } + }.onFailure { + Timber.e(it) + _autoTunnelActive.update { false } + } + withContext(mainDispatcher) { updateAutoTunnelTile() } + } + } + } + + fun stopAutoTunnel() { + applicationScope.launch(ioDispatcher) { + autoTunnelMutex.withLock { + val settings = appDataRepository.settings.get() + appDataRepository.settings.save(settings.copy(isAutoTunnelEnabled = false)) + if (!autoTunnelService.isCompleted) return@launch + runCatching { + val service = autoTunnelService.await() + service.stop() + _autoTunnelActive.update { false } + autoTunnelService = CompletableDeferred() + }.onFailure { + Timber.e(it) + } + withContext(mainDispatcher) { updateAutoTunnelTile() } } } - updateAutoTunnelTile() } fun startTunnelForegroundService(tunnelConf: TunnelConf) { @@ -125,23 +153,6 @@ class ServiceManager @Inject constructor( context.requestTunnelTileServiceStateUpdate() } - fun stopAutoTunnel() { - applicationScope.launch(ioDispatcher) { - val settings = appDataRepository.settings.get() - appDataRepository.settings.save(settings.copy(isAutoTunnelEnabled = false)) - if (!autoTunnelService.isCompleted) return@launch - runCatching { - val service = autoTunnelService.await() - service.stop() - _autoTunnelActive.update { false } - autoTunnelService = CompletableDeferred() - updateAutoTunnelTile() - }.onFailure { - Timber.e(it) - } - } - } - companion object { const val SERVICE_START_TIMEOUT = 5_000L } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/AutoTunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/AutoTunnelControlTile.kt index 9fc100d3..907d3c08 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/AutoTunnelControlTile.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/AutoTunnelControlTile.kt @@ -37,19 +37,18 @@ class AutoTunnelControlTile : TileService(), LifecycleOwner { override fun onStartListening() { super.onStartListening() - Timber.d("Start listening called for auto tunnel tile") lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) + Timber.d("Start listening called for auto tunnel tile") lifecycleScope.launch { serviceManager.autoTunnelActive.collect { - if (it) setActive() else setInactive() + if (it) return@collect setActive() + setInactive() } } lifecycleScope.launch { appDataRepository.tunnels.flow.collect { if (it.isEmpty()) { setUnavailable() - } else { - if (qsTile.state == Tile.STATE_ACTIVE) setInactive() } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/TunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/TunnelControlTile.kt index 2f9a172b..fcf9d11e 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/TunnelControlTile.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/core/service/tile/TunnelControlTile.kt @@ -59,12 +59,12 @@ class TunnelControlTile : TileService(), LifecycleOwner { } } - private fun updateTileState() = lifecycleScope.launch { + private suspend fun updateTileState() { try { val tunnels = appDataRepository.tunnels.getAll() if (tunnels.isEmpty()) { setUnavailable() - return@launch + return } val activeTunnels = tunnelManager.activeTunnels.value diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/di/TunnelModule.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/di/TunnelModule.kt index 786e8ca6..6f24c1e3 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/di/TunnelModule.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/di/TunnelModule.kt @@ -107,9 +107,10 @@ class TunnelModule { fun provideServiceManager( @ApplicationContext context: Context, @IoDispatcher ioDispatcher: CoroutineDispatcher, + @MainDispatcher mainCoroutineDispatcher: CoroutineDispatcher, @ApplicationScope applicationScope: CoroutineScope, appDataRepository: AppDataRepository, ): ServiceManager { - return ServiceManager(context, ioDispatcher, applicationScope, appDataRepository) + return ServiceManager(context, ioDispatcher, applicationScope, mainCoroutineDispatcher, appDataRepository) } }