From 64bb9f3b82c366f5edc5c8e73f2b653bec1a1a45 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Sun, 10 Sep 2023 00:22:36 -0400 Subject: [PATCH] fix: foreground service start crashes Attempt to fix startForegrounService causing crashes on some devices by not meeting the 5 second notification rule. Add notification to onCreate of services. Limit startForeground to only be called where it is truly necessary in the TileService to allow starting the VPN while app is not running. Attempt to manually initialize mlkit for QR code scanning to remediate some crashes caused by config scanning. --- app/build.gradle.kts | 2 +- .../WireGuardAutoTunnel.kt | 6 ++++ .../service/foreground/Action.kt | 1 + .../service/foreground/ForegroundService.kt | 2 +- .../service/foreground/ServiceManager.kt | 36 ++++++++++++++----- .../WireGuardConnectivityWatcherService.kt | 11 ++++-- .../foreground/WireGuardTunnelService.kt | 12 +++++-- .../service/tile/TunnelControlTile.kt | 4 +-- .../ui/screens/main/MainScreen.kt | 5 +-- build.gradle.kts | 2 +- 10 files changed, 61 insertions(+), 20 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1bb9ea6..2400539 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,7 +17,7 @@ android { val versionMajor = 2 val versionMinor = 4 - val versionPatch = 2 + val versionPatch = 3 val versionBuild = 0 defaultConfig { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt index bdd6c40..39204da 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.Context import android.content.pm.PackageManager import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.google.mlkit.common.MlKit import com.zaneschepke.wireguardautotunnel.repository.Repository import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings import dagger.hilt.android.HiltAndroidApp @@ -22,6 +23,11 @@ class WireGuardAutoTunnel : Application() { FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false); Timber.plant(Timber.DebugTree()) } + try { + MlKit.initialize(this) + } catch (e : Exception) { + Timber.e(e.message) + } settingsRepo.init() } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/Action.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/Action.kt index 1ccbfdf..e76f7ae 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/Action.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/Action.kt @@ -2,5 +2,6 @@ package com.zaneschepke.wireguardautotunnel.service.foreground enum class Action { START, + START_FOREGROUND, STOP } \ No newline at end of file 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 eb94d66..cbaf9af 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 @@ -22,7 +22,7 @@ open class ForegroundService : Service() { val action = intent.action Timber.d("using an intent with action $action") when (action) { - Action.START.name -> startService(intent.extras) + Action.START.name, Action.START_FOREGROUND.name -> startService(intent.extras) Action.STOP.name -> stopService(intent.extras) "android.net.VpnService" -> { Timber.d("Always-on VPN starting service") diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt index c410643..816eea5 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/ServiceManager.kt @@ -1,7 +1,6 @@ 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 @@ -9,7 +8,6 @@ import android.content.Intent import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase import com.zaneschepke.wireguardautotunnel.R -import timber.log.Timber object ServiceManager { @Suppress("DEPRECATION") @@ -36,13 +34,11 @@ object ServiceManager { intent.component?.javaClass try { when(action) { + Action.START_FOREGROUND -> { + context.startForegroundService(intent) + } Action.START -> { - try { - context.startForegroundService(intent) - } catch (e : Exception) { - Timber.e("Unable to start service foreground ${e.message}") - context.startService(intent) - } + context.startService(intent) } Action.STOP -> context.startService(intent) } @@ -66,6 +62,22 @@ object ServiceManager { ) } + fun startVpnServiceForeground(context : Context, tunnelConfig : String) { + actionOnService( + Action.START_FOREGROUND, + context, + WireGuardTunnelService::class.java, + mapOf(context.getString(R.string.tunnel_extras_key) to tunnelConfig)) + } + + private fun startWatcherServiceForeground(context : Context, tunnelConfig : String) { + actionOnService( + Action.START, context, + WireGuardConnectivityWatcherService::class.java, mapOf(context. + getString(R.string.tunnel_extras_key) to + tunnelConfig)) + } + fun startWatcherService(context : Context, tunnelConfig : String) { actionOnService( Action.START, context, @@ -87,4 +99,12 @@ object ServiceManager { ServiceState.STOPPED -> startWatcherService(context, tunnelConfig) } } + + fun toggleWatcherServiceForeground(context: Context, tunnelConfig : String) { + when(getServiceState( context, + WireGuardConnectivityWatcherService::class.java,)) { + ServiceState.STARTED -> stopWatcherService(context) + ServiceState.STOPPED -> startWatcherServiceForeground(context, tunnelConfig) + } + } } \ 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 4dac1a2..e24faec 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 @@ -59,8 +59,16 @@ class WireGuardConnectivityWatcherService : ForegroundService() { private val tag = this.javaClass.name; + override fun onCreate() { + super.onCreate() + CoroutineScope(Dispatchers.Main).launch { + launchWatcherNotification() + } + } + override fun startService(extras: Bundle?) { super.startService(extras) + launchWatcherNotification() val tunnelId = extras?.getString(getString(R.string.tunnel_extras_key)) if (tunnelId != null) { this.tunnelConfig = tunnelId @@ -68,7 +76,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() { // we need this lock so our service gets not affected by Doze Mode initWakeLock() cancelWatcherJob() - launchWatcherNotification() if(this::tunnelConfig.isInitialized) { startWatcherJob() } else { @@ -181,7 +188,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() { } private suspend fun manageVpn() { - while(watcherJob.isActive) { + while(true) { if(setting.isTunnelOnMobileDataEnabled && !isWifiConnected && isMobileDataConnected diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardTunnelService.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardTunnelService.kt index a43d2c8..b875591 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardTunnelService.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/foreground/WireGuardTunnelService.kt @@ -37,8 +37,16 @@ class WireGuardTunnelService : ForegroundService() { private var tunnelName : String = "" + override fun onCreate() { + super.onCreate() + CoroutineScope(Dispatchers.Main).launch { + launchVpnStartingNotification() + } + } + override fun startService(extras : Bundle?) { super.startService(extras) + launchVpnStartingNotification() val tunnelConfigString = extras?.getString(getString(R.string.tunnel_extras_key)) cancelJob() job = CoroutineScope(Dispatchers.IO).launch { @@ -47,7 +55,6 @@ class WireGuardTunnelService : ForegroundService() { val tunnelConfig = TunnelConfig.from(tunnelConfigString) tunnelName = tunnelConfig.name vpnService.startTunnel(tunnelConfig) - launchVpnStartingNotification() } catch (e : Exception) { Timber.e("Problem starting tunnel: ${e.message}") stopService(extras) @@ -61,7 +68,6 @@ class WireGuardTunnelService : ForegroundService() { val tunnelConfig = TunnelConfig.from(setting.defaultTunnel!!) tunnelName = tunnelConfig.name vpnService.startTunnel(tunnelConfig) - launchVpnStartingNotification() } } } @@ -100,7 +106,7 @@ class WireGuardTunnelService : ForegroundService() { override fun stopService(extras : Bundle?) { super.stopService(extras) - CoroutineScope(Dispatchers.IO).launch() { + CoroutineScope(Dispatchers.IO).launch { vpnService.stopTunnel() } cancelJob() diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt index aa211de..65e8469 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/service/tile/TunnelControlTile.kt @@ -66,7 +66,7 @@ class TunnelControlTile : TileService() { if(vpnService.getState() == Tunnel.State.UP) { ServiceManager.stopVpnService(this@TunnelControlTile) } else { - ServiceManager.startVpnService(this@TunnelControlTile, tunnel.toString()) + ServiceManager.startVpnServiceForeground(this@TunnelControlTile, tunnel.toString()) } } } catch (e : Exception) { @@ -100,7 +100,7 @@ class TunnelControlTile : TileService() { if (!settings.isNullOrEmpty()) { val setting = settings.first() if(setting.isAutoTunnelEnabled) { - ServiceManager.toggleWatcherService(this@TunnelControlTile, tunnelConfig) + ServiceManager.toggleWatcherServiceForeground(this@TunnelControlTile, tunnelConfig) } } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt index 162c497..afa9303 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/main/MainScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons @@ -103,6 +102,7 @@ fun MainScreen( val state by viewModel.state.collectAsStateWithLifecycle(Tunnel.State.DOWN) val tunnelName by viewModel.tunnelName.collectAsStateWithLifecycle("") + // Nested scroll for control FAB val nestedScrollConnection = remember { object : NestedScrollConnection { @@ -242,7 +242,8 @@ fun MainScreen( ) { LazyColumn( - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxSize() .nestedScroll(nestedScrollConnection), ) { itemsIndexed(tunnels.toList()) { index, tunnel -> diff --git a/build.gradle.kts b/build.gradle.kts index f9d7e05..f2df859 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ buildscript { } plugins { - id("com.android.application") version "8.2.0-beta01" apply false + id("com.android.application") version "8.2.0-beta03" apply false id("org.jetbrains.kotlin.android") version "1.8.22" apply false id("com.google.dagger.hilt.android") version "2.44" apply false kotlin("plugin.serialization") version "1.8.22" apply false