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 1bb9ea67..2400539e 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 bdd6c40e..39204daf 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 1ccbfdf5..e76f7aee 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 eb94d665..cbaf9af1 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 c4106439..816eea56 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 4dac1a24..e24faec2 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 a43d2c8c..b8755915 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 aa211de2..65e84696 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 162c4975..afa9303f 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 f9d7e05a..f2df8592 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