fix: add action and tunnel name to kernel notification (#490)

This commit is contained in:
Zane Schepke 2024-12-12 23:54:08 -05:00 committed by GitHub
parent 1120a8fc81
commit 62daf138dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 61 deletions

View File

@ -73,7 +73,6 @@ class WireGuardAutoTunnel : Application() {
if (!settingsRepository.getSettings().isKernelEnabled) { if (!settingsRepository.getSettings().isKernelEnabled) {
tunnelService.setBackendState(BackendState.SERVICE_ACTIVE, emptyList()) tunnelService.setBackendState(BackendState.SERVICE_ACTIVE, emptyList())
} }
appStateRepository.getLocale()?.let { appStateRepository.getLocale()?.let {
LocaleUtil.changeLocale(it) LocaleUtil.changeLocale(it)
} }

View File

@ -3,6 +3,7 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.AutoTunnelService import com.zaneschepke.wireguardautotunnel.service.foreground.autotunnel.AutoTunnelService
import com.zaneschepke.wireguardautotunnel.service.tile.AutoTunnelControlTile import com.zaneschepke.wireguardautotunnel.service.tile.AutoTunnelControlTile
@ -58,12 +59,12 @@ class ServiceManager
} }
} }
suspend fun startBackgroundService() { suspend fun startBackgroundService(tunnelConfig: TunnelConfig?) {
if (backgroundService.isCompleted) return if (backgroundService.isCompleted) return
kotlin.runCatching { kotlin.runCatching {
startService(TunnelBackgroundService::class.java, true) startService(TunnelBackgroundService::class.java, true)
backgroundService.await() backgroundService.await()
backgroundService.getCompleted().start() backgroundService.getCompleted().start(tunnelConfig)
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
} }
@ -85,7 +86,7 @@ class ServiceManager
} }
} }
fun updateAutoTunnelTile() { private fun updateAutoTunnelTile() {
if (autoTunnelTile.isCompleted) { if (autoTunnelTile.isCompleted) {
autoTunnelTile.getCompleted().updateTileState() autoTunnelTile.getCompleted().updateTileState()
} else { } else {

View File

@ -6,6 +6,8 @@ import android.os.IBinder
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.lifecycle.LifecycleService import androidx.lifecycle.LifecycleService
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationAction
import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService import com.zaneschepke.wireguardautotunnel.service.notification.NotificationService
import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification import com.zaneschepke.wireguardautotunnel.service.notification.WireGuardNotification
import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.Constants
@ -24,12 +26,11 @@ class TunnelBackgroundService : LifecycleService() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
start() serviceManager.backgroundService.complete(this)
} }
override fun onBind(intent: Intent): IBinder? { override fun onBind(intent: Intent): IBinder? {
super.onBind(intent) super.onBind(intent)
// We don't provide binding, so return null
return null return null
} }
@ -38,11 +39,11 @@ class TunnelBackgroundService : LifecycleService() {
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
} }
fun start() { fun start(tunnelConfig: TunnelConfig?) {
ServiceCompat.startForeground( ServiceCompat.startForeground(
this, this,
NotificationService.KERNEL_SERVICE_NOTIFICATION_ID, NotificationService.KERNEL_SERVICE_NOTIFICATION_ID,
createNotification(), createNotification(tunnelConfig),
Constants.SYSTEM_EXEMPT_SERVICE_TYPE_ID, Constants.SYSTEM_EXEMPT_SERVICE_TYPE_ID,
) )
} }
@ -57,11 +58,13 @@ class TunnelBackgroundService : LifecycleService() {
super.onDestroy() super.onDestroy()
} }
private fun createNotification(): Notification { private fun createNotification(tunnelConfig: TunnelConfig?): Notification {
return notificationService.createNotification( return notificationService.createNotification(
WireGuardNotification.NotificationChannels.VPN, WireGuardNotification.NotificationChannels.VPN,
getString(R.string.tunnel_running), title = "${getString(R.string.tunnel_running)} - ${tunnelConfig?.name}",
description = "", actions = listOf(
notificationService.createNotificationAction(NotificationAction.TUNNEL_OFF),
),
) )
} }
} }

View File

@ -85,7 +85,6 @@ constructor(
private suspend fun setState(tunnelConfig: TunnelConfig, tunnelState: TunnelState): Result<TunnelState> { private suspend fun setState(tunnelConfig: TunnelConfig, tunnelState: TunnelState): Result<TunnelState> {
return runCatching { return runCatching {
updateTunnelConfig(tunnelConfig) //need so kernel can get tunnel name or it breaks kernel
when (val backend = backend()) { when (val backend = backend()) {
is Backend -> backend.setState(this, tunnelState.toWgState(), TunnelConfig.configFromWgQuick(tunnelConfig.wgQuick)).let { TunnelState.from(it) } is Backend -> backend.setState(this, tunnelState.toWgState(), TunnelConfig.configFromWgQuick(tunnelConfig.wgQuick)).let { TunnelState.from(it) }
is org.amnezia.awg.backend.Backend -> { is org.amnezia.awg.backend.Backend -> {
@ -103,7 +102,7 @@ constructor(
else -> throw NotImplementedError() else -> throw NotImplementedError()
} }
}.onFailure { }.onFailure {
//TODO add better error message and comms to user // TODO add better error message and comms to user
Timber.e(it) Timber.e(it)
} }
} }
@ -115,26 +114,15 @@ constructor(
} }
override suspend fun startTunnel(tunnelConfig: TunnelConfig?, background: Boolean) { override suspend fun startTunnel(tunnelConfig: TunnelConfig?, background: Boolean) {
if (tunnelConfig == null) return
withContext(ioDispatcher) { withContext(ioDispatcher) {
if (isTunnelAlreadyRunning(tunnelConfig)) return@withContext if (tunnelConfig == null || isTunnelAlreadyRunning(tunnelConfig)) return@withContext
updateTunnelConfig(tunnelConfig) // need to update this here
withServiceActive { withServiceActive {
onBeforeStart(background) onBeforeStart()
tunnelControlMutex.withLock { tunnelControlMutex.withLock {
setState(tunnelConfig, TunnelState.UP).onSuccess { setState(tunnelConfig, TunnelState.UP).onSuccess {
startActiveTunnelJobs()
if (it.isUp()) appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true))
with(notificationService) {
val notification = createNotification(
WireGuardNotification.NotificationChannels.VPN,
title = "${context.getString(R.string.tunnel_running)} - ${tunnelConfig.name}",
actions = listOf(
notificationService.createNotificationAction(NotificationAction.TUNNEL_OFF),
),
)
show(VPN_NOTIFICATION_ID, notification)
}
updateTunnelState(it, tunnelConfig) updateTunnelState(it, tunnelConfig)
onTunnelStart(tunnelConfig)
} }
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
@ -150,10 +138,8 @@ constructor(
if (tunnelConfig == null) return@withContext if (tunnelConfig == null) return@withContext
tunnelControlMutex.withLock { tunnelControlMutex.withLock {
setState(tunnelConfig, TunnelState.DOWN).onSuccess { setState(tunnelConfig, TunnelState.DOWN).onSuccess {
onTunnelStop(tunnelConfig)
updateTunnelState(it, null) updateTunnelState(it, null)
onStop(tunnelConfig)
notificationService.remove(VPN_NOTIFICATION_ID)
stopBackgroundService()
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
} }
@ -218,33 +204,45 @@ constructor(
} }
} }
private suspend fun shutDownActiveTunnel() { private suspend fun onBeforeStart() {
with(_vpnState.value) { with(_vpnState.value) {
if (status.isUp()) { if (status.isUp()) stopTunnel() else clearJobsAndStats()
stopTunnel() if (isKernelBackend == true) serviceManager.startBackgroundService(tunnelConfig)
}
} }
} }
private suspend fun startBackgroundService() { private suspend fun onTunnelStart(tunnelConfig: TunnelConfig) {
serviceManager.startBackgroundService() startActiveTunnelJobs()
serviceManager.updateTunnelTile() if (_vpnState.value.status.isUp()) {
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true))
}
if (isKernelBackend == false) launchUserspaceTunnelNotification()
} }
private fun stopBackgroundService() { private fun launchUserspaceTunnelNotification() {
serviceManager.stopBackgroundService() with(notificationService) {
serviceManager.updateTunnelTile() val notification = createNotification(
WireGuardNotification.NotificationChannels.VPN,
title = "${context.getString(R.string.tunnel_running)} - ${_vpnState.value.tunnelConfig?.name}",
actions = listOf(
notificationService.createNotificationAction(NotificationAction.TUNNEL_OFF),
),
)
show(VPN_NOTIFICATION_ID, notification)
}
} }
private suspend fun onBeforeStart(background: Boolean) { private suspend fun onTunnelStop(tunnelConfig: TunnelConfig) {
shutDownActiveTunnel()
resetBackendStatistics()
val settings = appDataRepository.settings.getSettings()
if (background || settings.isKernelEnabled) startBackgroundService()
}
private suspend fun onStop(tunnelConfig: TunnelConfig) {
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = false)) appDataRepository.tunnels.save(tunnelConfig.copy(isActive = false))
if (isKernelBackend == true) {
serviceManager.stopBackgroundService()
} else {
notificationService.remove(VPN_NOTIFICATION_ID)
}
clearJobsAndStats()
}
private fun clearJobsAndStats() {
cancelActiveTunnelJobs() cancelActiveTunnelJobs()
resetBackendStatistics() resetBackendStatistics()
} }
@ -303,14 +301,12 @@ constructor(
is Backend -> updateBackendStatistics( is Backend -> updateBackendStatistics(
WireGuardStatistics(backend.getStatistics(this@WireGuardTunnel)), WireGuardStatistics(backend.getStatistics(this@WireGuardTunnel)),
) )
is org.amnezia.awg.backend.Backend -> { is org.amnezia.awg.backend.Backend -> updateBackendStatistics(
updateBackendStatistics(
AmneziaStatistics( AmneziaStatistics(
backend.getStatistics(this@WireGuardTunnel), backend.getStatistics(this@WireGuardTunnel),
), ),
) )
} }
}
delay(VPN_STATISTIC_CHECK_INTERVAL) delay(VPN_STATISTIC_CHECK_INTERVAL)
} }
} }

View File

@ -41,8 +41,8 @@ fun KillSwitchScreen(uiState: AppUiState, appViewModel: AppViewModel) {
fun toggleVpnKillSwitch() { fun toggleVpnKillSwitch() {
with(uiState.settings) { with(uiState.settings) {
//TODO improve this error message // TODO improve this error message
if(isKernelEnabled) return SnackbarController.showMessage(StringValue.StringResource(R.string.kernel_not_supported)) if (isKernelEnabled) return SnackbarController.showMessage(StringValue.StringResource(R.string.kernel_not_supported))
if (isVpnKillSwitchEnabled) { if (isVpnKillSwitchEnabled) {
appViewModel.onToggleVpnKillSwitch(false) appViewModel.onToggleVpnKillSwitch(false)
} else { } else {

View File

@ -16,7 +16,7 @@ junit = "4.13.2"
kotlinx-serialization-json = "1.7.3" kotlinx-serialization-json = "1.7.3"
lifecycle-runtime-compose = "2.8.7" lifecycle-runtime-compose = "2.8.7"
material3 = "1.3.1" material3 = "1.3.1"
navigationCompose = "2.8.4" navigationCompose = "2.8.5"
pinLockCompose = "1.0.4" pinLockCompose = "1.0.4"
roomVersion = "2.6.1" roomVersion = "2.6.1"
timber = "5.0.1" timber = "5.0.1"
@ -24,8 +24,8 @@ tunnel = "1.2.1"
androidGradlePlugin = "8.8.0-rc01" androidGradlePlugin = "8.8.0-rc01"
kotlin = "2.1.0" kotlin = "2.1.0"
ksp = "2.1.0-1.0.29" ksp = "2.1.0-1.0.29"
composeBom = "2024.11.00" composeBom = "2024.12.01"
compose = "1.7.5" compose = "1.7.6"
zxingAndroidEmbedded = "4.3.0" zxingAndroidEmbedded = "4.3.0"
coreSplashscreen = "1.0.1" coreSplashscreen = "1.0.1"
gradlePlugins-grgit = "5.3.0" gradlePlugins-grgit = "5.3.0"