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.
This commit is contained in:
Zane Schepke 2023-09-10 00:22:36 -04:00
parent c1b560e822
commit 64bb9f3b82
10 changed files with 61 additions and 20 deletions

View File

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

View File

@ -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()
}

View File

@ -2,5 +2,6 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
enum class Action {
START,
START_FOREGROUND,
STOP
}

View File

@ -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")

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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()

View File

@ -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)
}
}
}

View File

@ -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 ->

View File

@ -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