Compare commits

...

5 Commits

Author SHA1 Message Date
GitHub Actions 5cc2ae0d01 Automated build update 2024-11-08 04:14:02 +00:00
Zane Schepke 7fdec509e9 Merge branch 'main' of https://github.com/zaneschepke/wgtunnel 2024-11-07 23:03:02 -05:00
Zane Schepke cab2945930 fix: auto tunnel bugs
Fixes auto tunnel bug that can happen on startup
Fixes auto tunnel bug that didn't allow manual toggle override of tunnel while auto tunnel is active
Add basic foreground persistent notifications (optionally turned off via android settings)
2024-11-07 23:03:00 -05:00
GitHub Actions 6a90cd02b9 Automated build update 2024-11-08 03:30:59 +00:00
Zane Schepke 7d810c7c3d fix: auto tunnel crash 2024-11-07 20:02:35 -05:00
13 changed files with 127 additions and 87 deletions
@@ -9,3 +9,11 @@ annotation class Kernel
@Qualifier @Qualifier
@Retention(AnnotationRetention.BINARY) @Retention(AnnotationRetention.BINARY)
annotation class Userspace annotation class Userspace
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class TunnelShell
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AppShell
@@ -25,9 +25,18 @@ import javax.inject.Singleton
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
class TunnelModule { class TunnelModule {
@Provides @Provides
@Singleton @Singleton
fun provideRootShell(@ApplicationContext context: Context): RootShell { @TunnelShell
fun provideTunnelRootShell(@ApplicationContext context: Context): RootShell {
return RootShell(context)
}
@Provides
@Singleton
@AppShell
fun provideAppRootShell(@ApplicationContext context: Context): RootShell {
return RootShell(context) return RootShell(context)
} }
@@ -40,14 +49,14 @@ class TunnelModule {
@Provides @Provides
@Singleton @Singleton
@Userspace @Userspace
fun provideUserspaceBackend(@ApplicationContext context: Context, rootShell: RootShell): Backend { fun provideUserspaceBackend(@ApplicationContext context: Context, @TunnelShell rootShell: RootShell): Backend {
return GoBackend(context, RootTunnelActionHandler(rootShell)) return GoBackend(context, RootTunnelActionHandler(rootShell))
} }
@Provides @Provides
@Singleton @Singleton
@Kernel @Kernel
fun provideKernelBackend(@ApplicationContext context: Context, rootShell: RootShell): Backend { fun provideKernelBackend(@ApplicationContext context: Context, @TunnelShell rootShell: RootShell): Backend {
return WgQuickBackend(context, rootShell, ToolsInstaller(context, rootShell), RootTunnelActionHandler(rootShell)) return WgQuickBackend(context, rootShell, ToolsInstaller(context, rootShell), RootTunnelActionHandler(rootShell))
} }
@@ -82,6 +91,6 @@ class TunnelModule {
@Singleton @Singleton
@Provides @Provides
fun provideServiceManager(@ApplicationContext context: Context): ServiceManager { fun provideServiceManager(@ApplicationContext context: Context): ServiceManager {
return ServiceManager(context) return ServiceManager.getInstance(context)
} }
} }
@@ -1,6 +1,5 @@
package com.zaneschepke.wireguardautotunnel.service.foreground package com.zaneschepke.wireguardautotunnel.service.foreground
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.IBinder import android.os.IBinder
@@ -13,6 +12,7 @@ import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.Settings import com.zaneschepke.wireguardautotunnel.data.domain.Settings
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig 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.module.AppShell
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.module.MainImmediateDispatcher import com.zaneschepke.wireguardautotunnel.module.MainImmediateDispatcher
import com.zaneschepke.wireguardautotunnel.service.network.EthernetService import com.zaneschepke.wireguardautotunnel.service.network.EthernetService
@@ -26,7 +26,6 @@ import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage import com.zaneschepke.wireguardautotunnel.util.extensions.cancelWithMessage
import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName import com.zaneschepke.wireguardautotunnel.util.extensions.getCurrentWifiName
import com.zaneschepke.wireguardautotunnel.util.extensions.isDown
import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable import com.zaneschepke.wireguardautotunnel.util.extensions.isReachable
import com.zaneschepke.wireguardautotunnel.util.extensions.onNotRunning import com.zaneschepke.wireguardautotunnel.util.extensions.onNotRunning
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -35,7 +34,6 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@@ -51,6 +49,7 @@ class AutoTunnelService : LifecycleService() {
private val foregroundId = 122 private val foregroundId = 122
@Inject @Inject
@AppShell
lateinit var rootShell: Provider<RootShell> lateinit var rootShell: Provider<RootShell>
@Inject @Inject
@@ -144,7 +143,7 @@ class AutoTunnelService : LifecycleService() {
super.onDestroy() super.onDestroy()
} }
private fun launchWatcherNotification(description: String = getString(R.string.watcher_notification_text_active)) { private fun launchWatcherNotification(description: String = getString(R.string.monitoring_state_changes)) {
val notification = val notification =
notificationService.createNotification( notificationService.createNotification(
channelId = getString(R.string.watcher_channel_id), channelId = getString(R.string.watcher_channel_id),
@@ -162,7 +161,7 @@ class AutoTunnelService : LifecycleService() {
private fun initWakeLock() { private fun initWakeLock() {
wakeLock = wakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run { (getSystemService(POWER_SERVICE) as PowerManager).run {
val tag = this.javaClass.name val tag = this.javaClass.name
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag::lock").apply { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$tag::lock").apply {
try { try {
@@ -210,28 +209,16 @@ class AutoTunnelService : LifecycleService() {
when (status) { when (status) {
is NetworkStatus.Available -> { is NetworkStatus.Available -> {
Timber.i("Gained Mobile data connection") Timber.i("Gained Mobile data connection")
autoTunnelStateFlow.update { emitMobileDataConnected(true)
it.copy(
isMobileDataConnected = true,
)
}
} }
is NetworkStatus.CapabilitiesChanged -> { is NetworkStatus.CapabilitiesChanged -> {
autoTunnelStateFlow.update { emitMobileDataConnected(true)
it.copy(
isMobileDataConnected = true,
)
}
Timber.i("Mobile data capabilities changed") Timber.i("Mobile data capabilities changed")
} }
is NetworkStatus.Unavailable -> { is NetworkStatus.Unavailable -> {
autoTunnelStateFlow.update { emitMobileDataConnected(false)
it.copy(
isMobileDataConnected = false,
)
}
Timber.i("Lost mobile data connection") Timber.i("Lost mobile data connection")
} }
} }
@@ -284,6 +271,7 @@ class AutoTunnelService : LifecycleService() {
old.map { it.isActive } != new.map { it.isActive } old.map { it.isActive } != new.map { it.isActive }
}, },
) { settings, tunnels -> ) { settings, tunnels ->
Timber.d("Tunnels or settings changed!")
autoTunnelStateFlow.value.copy( autoTunnelStateFlow.value.copy(
settings = settings, settings = settings,
tunnels = tunnels, tunnels = tunnels,
@@ -300,7 +288,7 @@ class AutoTunnelService : LifecycleService() {
Timber.i("Starting vpn state watcher") Timber.i("Starting vpn state watcher")
withContext(ioDispatcher) { withContext(ioDispatcher) {
tunnelService.get().vpnState.distinctUntilChanged { old, new -> tunnelService.get().vpnState.distinctUntilChanged { old, new ->
old.tunnelConfig == new.tunnelConfig && old.status == new.status old.tunnelConfig?.id == new.tunnelConfig?.id
}.collect { state -> }.collect { state ->
autoTunnelStateFlow.update { autoTunnelStateFlow.update {
it.copy(vpnState = state) it.copy(vpnState = state)
@@ -368,7 +356,7 @@ class AutoTunnelService : LifecycleService() {
mobileDataJob = null mobileDataJob = null
} }
private fun updateEthernet(connected: Boolean) { private fun emitEthernetConnected(connected: Boolean) {
autoTunnelStateFlow.update { autoTunnelStateFlow.update {
it.copy( it.copy(
isEthernetConnected = connected, isEthernetConnected = connected,
@@ -376,7 +364,7 @@ class AutoTunnelService : LifecycleService() {
} }
} }
private fun updateWifi(connected: Boolean) { private fun emitWifiConnected(connected: Boolean) {
autoTunnelStateFlow.update { autoTunnelStateFlow.update {
it.copy( it.copy(
isWifiConnected = connected, isWifiConnected = connected,
@@ -384,6 +372,22 @@ class AutoTunnelService : LifecycleService() {
} }
} }
private fun emitWifiSSID(ssid: String) {
autoTunnelStateFlow.update {
it.copy(
currentNetworkSSID = ssid,
)
}
}
private fun emitMobileDataConnected(connected: Boolean) {
autoTunnelStateFlow.update {
it.copy(
isMobileDataConnected = connected,
)
}
}
private suspend fun watchForEthernetConnectivityChanges() { private suspend fun watchForEthernetConnectivityChanges() {
withContext(ioDispatcher) { withContext(ioDispatcher) {
Timber.i("Starting ethernet data watcher") Timber.i("Starting ethernet data watcher")
@@ -391,16 +395,16 @@ class AutoTunnelService : LifecycleService() {
when (status) { when (status) {
is NetworkStatus.Available -> { is NetworkStatus.Available -> {
Timber.i("Gained Ethernet connection") Timber.i("Gained Ethernet connection")
updateEthernet(true) emitEthernetConnected(true)
} }
is NetworkStatus.CapabilitiesChanged -> { is NetworkStatus.CapabilitiesChanged -> {
Timber.i("Ethernet capabilities changed") Timber.i("Ethernet capabilities changed")
updateEthernet(true) emitEthernetConnected(true)
} }
is NetworkStatus.Unavailable -> { is NetworkStatus.Unavailable -> {
updateEthernet(false) emitEthernetConnected(false)
Timber.i("Lost Ethernet connection") Timber.i("Lost Ethernet connection")
} }
} }
@@ -415,12 +419,12 @@ class AutoTunnelService : LifecycleService() {
when (status) { when (status) {
is NetworkStatus.Available -> { is NetworkStatus.Available -> {
Timber.i("Gained Wi-Fi connection") Timber.i("Gained Wi-Fi connection")
updateWifi(true) emitWifiConnected(true)
} }
is NetworkStatus.CapabilitiesChanged -> { is NetworkStatus.CapabilitiesChanged -> {
Timber.i("Wifi capabilities changed") Timber.i("Wifi capabilities changed")
updateWifi(true) emitWifiConnected(true)
val ssid = getWifiSSID(status.networkCapabilities) val ssid = getWifiSSID(status.networkCapabilities)
ssid?.let { name -> ssid?.let { name ->
if (name.contains(Constants.UNREADABLE_SSID)) { if (name.contains(Constants.UNREADABLE_SSID)) {
@@ -429,16 +433,12 @@ class AutoTunnelService : LifecycleService() {
Timber.i("Detected valid SSID") Timber.i("Detected valid SSID")
} }
appDataRepository.appState.setCurrentSsid(name) appDataRepository.appState.setCurrentSsid(name)
autoTunnelStateFlow.update { emitWifiSSID(name)
it.copy(
currentNetworkSSID = name,
)
}
} ?: Timber.w("Failed to read ssid") } ?: Timber.w("Failed to read ssid")
} }
is NetworkStatus.Unavailable -> { is NetworkStatus.Unavailable -> {
updateWifi(false) emitWifiConnected(false)
Timber.i("Lost Wi-Fi connection") Timber.i("Lost Wi-Fi connection")
} }
} }
@@ -462,17 +462,17 @@ class AutoTunnelService : LifecycleService() {
private suspend fun handleNetworkEventChanges() { private suspend fun handleNetworkEventChanges() {
withContext(ioDispatcher) { withContext(ioDispatcher) {
Timber.i("Starting network event watcher") Timber.i("Starting network event watcher")
autoTunnelStateFlow.collectLatest { watcherState -> autoTunnelStateFlow.collect { watcherState ->
val autoTunnel = "Auto-tunnel watcher" val autoTunnel = "Auto-tunnel watcher"
Timber.d("New watcher state!")
// delay for rapid network state changes and then collect latest // delay for rapid network state changes and then collect latest
delay(Constants.WATCHER_COLLECTION_DELAY) delay(Constants.WATCHER_COLLECTION_DELAY)
val activeTunnel = watcherState.vpnState.tunnelConfig val activeTunnel = watcherState.vpnState.tunnelConfig
val defaultTunnel = appDataRepository.getPrimaryOrFirstTunnel() val defaultTunnel = appDataRepository.getPrimaryOrFirstTunnel()
val isTunnelDown = tunnelService.get().getState() == TunnelState.DOWN
when { when {
watcherState.isEthernetConditionMet() -> { watcherState.isEthernetConditionMet() -> {
Timber.i("$autoTunnel - tunnel on on ethernet condition met") Timber.i("$autoTunnel - tunnel on on ethernet condition met")
if (watcherState.vpnState.isDown()) { if (isTunnelDown) {
defaultTunnel?.let { defaultTunnel?.let {
tunnelService.get().startTunnel(it) tunnelService.get().startTunnel(it)
} }
@@ -484,7 +484,7 @@ class AutoTunnelService : LifecycleService() {
val mobileDataTunnel = getMobileDataTunnel() val mobileDataTunnel = getMobileDataTunnel()
val tunnel = val tunnel =
mobileDataTunnel ?: defaultTunnel mobileDataTunnel ?: defaultTunnel
if (watcherState.vpnState.isDown() || activeTunnel?.isMobileDataTunnel == false) { if (isTunnelDown || activeTunnel?.isMobileDataTunnel == false) {
tunnel?.let { tunnel?.let {
tunnelService.get().startTunnel(it) tunnelService.get().startTunnel(it)
} }
@@ -493,7 +493,7 @@ class AutoTunnelService : LifecycleService() {
watcherState.isTunnelOffOnMobileDataConditionMet() -> { watcherState.isTunnelOffOnMobileDataConditionMet() -> {
Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off") Timber.i("$autoTunnel - tunnel off on mobile data met, turning vpn off")
if (!watcherState.vpnState.isDown()) { if (!isTunnelDown) {
activeTunnel?.let { activeTunnel?.let {
tunnelService.get().stopTunnel(it) tunnelService.get().stopTunnel(it)
} }
@@ -503,20 +503,20 @@ class AutoTunnelService : LifecycleService() {
watcherState.isUntrustedWifiConditionMet() -> { watcherState.isUntrustedWifiConditionMet() -> {
Timber.i("Untrusted wifi condition met") Timber.i("Untrusted wifi condition met")
if (activeTunnel == null || watcherState.isCurrentSSIDActiveTunnelNetwork() == false || if (activeTunnel == null || watcherState.isCurrentSSIDActiveTunnelNetwork() == false ||
watcherState.vpnState.isDown() isTunnelDown
) { ) {
Timber.i( Timber.i(
"$autoTunnel - tunnel on ssid not associated with current tunnel condition met", "$autoTunnel - tunnel on ssid not associated with current tunnel condition met",
) )
watcherState.getTunnelWithMatchingTunnelNetwork()?.let { watcherState.getTunnelWithMatchingTunnelNetwork()?.let {
Timber.i("Found tunnel associated with this SSID, bringing tunnel up: ${it.name}") Timber.i("Found tunnel associated with this SSID, bringing tunnel up: ${it.name}")
if (watcherState.vpnState.isDown() || activeTunnel?.id != it.id) { if (isTunnelDown || activeTunnel?.id != it.id) {
tunnelService.get().startTunnel(it) tunnelService.get().startTunnel(it)
} }
} ?: suspend { } ?: suspend {
Timber.i("No tunnel associated with this SSID, using defaults") Timber.i("No tunnel associated with this SSID, using defaults")
val default = appDataRepository.getPrimaryOrFirstTunnel() val default = appDataRepository.getPrimaryOrFirstTunnel()
if (default?.name != tunnelService.get().name || watcherState.vpnState.isDown()) { if (default?.name != tunnelService.get().name || isTunnelDown) {
default?.let { default?.let {
tunnelService.get().startTunnel(it) tunnelService.get().startTunnel(it)
} }
@@ -529,22 +529,22 @@ class AutoTunnelService : LifecycleService() {
Timber.i( Timber.i(
"$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off", "$autoTunnel - tunnel off on trusted wifi condition met, turning vpn off",
) )
if (!watcherState.vpnState.isDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) } if (!isTunnelDown) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
} }
watcherState.isTunnelOffOnWifiConditionMet() -> { watcherState.isTunnelOffOnWifiConditionMet() -> {
Timber.i( Timber.i(
"$autoTunnel - tunnel off on wifi condition met, turning vpn off", "$autoTunnel - tunnel off on wifi condition met, turning vpn off",
) )
if (!watcherState.vpnState.isDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) } if (!isTunnelDown) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
}
watcherState.isTunnelOffOnNoConnectivityMet() -> {
Timber.i(
"$autoTunnel - tunnel off on no connectivity met, turning vpn off",
)
if (!watcherState.vpnState.isDown()) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
} }
// TODO disable for this now
// watcherState.isTunnelOffOnNoConnectivityMet() -> {
// Timber.i(
// "$autoTunnel - tunnel off on no connectivity met, turning vpn off",
// )
// if (!isTunnelDown) activeTunnel?.let { tunnelService.get().stopTunnel(it) }
// }
else -> { else -> {
Timber.i("$autoTunnel - no condition met") Timber.i("$autoTunnel - no condition met")
@@ -27,12 +27,14 @@ class ServiceManager
companion object : SingletonHolder<ServiceManager, Context>(::ServiceManager) companion object : SingletonHolder<ServiceManager, Context>(::ServiceManager)
private fun <T : Service> startService(cls: Class<T>, background: Boolean) { private fun <T : Service> startService(cls: Class<T>, background: Boolean) {
val intent = Intent(context, cls) runCatching {
if (background) { val intent = Intent(context, cls)
context.startForegroundService(intent) if (background) {
} else { context.startForegroundService(intent)
context.startService(intent) } else {
} context.startService(intent)
}
}.onFailure { Timber.e(it) }
} }
suspend fun startAutoTunnel(background: Boolean) { suspend fun startAutoTunnel(background: Boolean) {
@@ -62,7 +62,7 @@ class TunnelBackgroundService : LifecycleService() {
return notificationService.createNotification( return notificationService.createNotification(
getString(R.string.vpn_channel_id), getString(R.string.vpn_channel_id),
getString(R.string.vpn_channel_name), getString(R.string.vpn_channel_name),
getString(R.string.tunnel_start_text), getString(R.string.tunnel_running),
description = "", description = "",
) )
} }
@@ -25,6 +25,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.amnezia.awg.backend.Tunnel import org.amnezia.awg.backend.Tunnel
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
@@ -52,6 +53,8 @@ constructor(
private var statsJob: Job? = null private var statsJob: Job? = null
private val runningHandle = AtomicBoolean(false)
private suspend fun backend(): Any { private suspend fun backend(): Any {
val settings = appDataRepository.settings.getSettings() val settings = appDataRepository.settings.getSettings()
if (settings.isKernelEnabled) return kernelBackend.get() if (settings.isKernelEnabled) return kernelBackend.get()
@@ -91,7 +94,14 @@ constructor(
override suspend fun startTunnel(tunnelConfig: TunnelConfig, background: Boolean): Result<TunnelState> { override suspend fun startTunnel(tunnelConfig: TunnelConfig, background: Boolean): Result<TunnelState> {
return withContext(ioDispatcher) { return withContext(ioDispatcher) {
onBeforeStart(tunnelConfig, background) if (runningHandle.get() == true && tunnelConfig == vpnState.value.tunnelConfig) {
Timber.w("Tunnel already running")
return@withContext Result.success(vpnState.value.status)
}
runningHandle.set(true)
onBeforeStart(tunnelConfig)
val settings = appDataRepository.settings.getSettings()
if (background || settings.isKernelEnabled) startBackgroundService()
setState(tunnelConfig, TunnelState.UP).onSuccess { setState(tunnelConfig, TunnelState.UP).onSuccess {
emitTunnelState(it) emitTunnelState(it)
}.onFailure { }.onFailure {
@@ -109,6 +119,9 @@ constructor(
}.onFailure { }.onFailure {
Timber.e(it) Timber.e(it)
onStopFailed() onStopFailed()
}.also {
stopBackgroundService()
runningHandle.set(false)
} }
} }
} }
@@ -143,30 +156,39 @@ constructor(
} }
cancelStatsJob() cancelStatsJob()
resetBackendStatistics() resetBackendStatistics()
runningHandle.set(false)
} }
private suspend fun onBeforeStart(tunnelConfig: TunnelConfig, background: Boolean) { private suspend fun shutDownActiveTunnel(config: TunnelConfig) {
if (_vpnState.value.status == TunnelState.UP && with(_vpnState.value) {
tunnelConfig != _vpnState.value.tunnelConfig if (status == TunnelState.UP && tunnelConfig != config) {
) { tunnelConfig?.let { stopTunnel(it) }
vpnState.value.tunnelConfig?.let { stopTunnel(it) } }
} }
if (background) serviceManager.startBackgroundService() }
resetBackendStatistics()
private suspend fun startBackgroundService() {
serviceManager.startBackgroundService()
serviceManager.requestTunnelTileUpdate()
}
private fun stopBackgroundService() {
serviceManager.stopBackgroundService()
serviceManager.requestTunnelTileUpdate()
}
private suspend fun onBeforeStart(tunnelConfig: TunnelConfig) {
shutDownActiveTunnel(tunnelConfig)
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true)) appDataRepository.tunnels.save(tunnelConfig.copy(isActive = true))
emitVpnStateConfig(tunnelConfig) emitVpnStateConfig(tunnelConfig)
resetBackendStatistics()
startStatsJob() startStatsJob()
Timber.d("Updating start")
serviceManager.requestTunnelTileUpdate()
} }
private suspend fun onBeforeStop(tunnelConfig: TunnelConfig) { private suspend fun onBeforeStop(tunnelConfig: TunnelConfig) {
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = false))
cancelStatsJob() cancelStatsJob()
resetBackendStatistics() resetBackendStatistics()
appDataRepository.tunnels.save(tunnelConfig.copy(isActive = false))
serviceManager.stopBackgroundService()
Timber.d("UPdating stop")
serviceManager.requestTunnelTileUpdate()
} }
private fun emitTunnelState(state: TunnelState) { private fun emitTunnelState(state: TunnelState) {
@@ -39,6 +39,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig import com.zaneschepke.wireguardautotunnel.data.domain.TunnelConfig
@@ -60,6 +61,7 @@ import com.zaneschepke.wireguardautotunnel.util.Constants
import com.zaneschepke.wireguardautotunnel.util.extensions.isBatteryOptimizationsDisabled import com.zaneschepke.wireguardautotunnel.util.extensions.isBatteryOptimizationsDisabled
import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv import com.zaneschepke.wireguardautotunnel.util.extensions.isRunningOnTv
import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl
import com.zaneschepke.wireguardautotunnel.util.extensions.scaledHeight
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@@ -213,7 +215,7 @@ fun MainScreen(viewModel: MainViewModel = hiltViewModel(), uiState: AppUiState)
) )
LazyColumn( LazyColumn(
horizontalAlignment = Alignment.Start, horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.spacedBy(5.dp.scaledHeight(), Alignment.Top),
modifier = modifier =
Modifier Modifier
.fillMaxSize().padding(it) .fillMaxSize().padding(it)
@@ -9,6 +9,7 @@ import com.wireguard.android.util.RootShell
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.Settings import com.zaneschepke.wireguardautotunnel.data.domain.Settings
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.AppShell
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.util.FileUtils import com.zaneschepke.wireguardautotunnel.util.FileUtils
@@ -29,7 +30,7 @@ class SettingsViewModel
@Inject @Inject
constructor( constructor(
private val appDataRepository: AppDataRepository, private val appDataRepository: AppDataRepository,
private val rootShell: Provider<RootShell>, @AppShell private val rootShell: Provider<RootShell>,
private val fileUtils: FileUtils, private val fileUtils: FileUtils,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher, @IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() { ) : ViewModel() {
@@ -6,6 +6,7 @@ import com.wireguard.android.util.RootShell
import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.data.domain.Settings import com.zaneschepke.wireguardautotunnel.data.domain.Settings
import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository import com.zaneschepke.wireguardautotunnel.data.repository.AppDataRepository
import com.zaneschepke.wireguardautotunnel.module.AppShell
import com.zaneschepke.wireguardautotunnel.module.IoDispatcher import com.zaneschepke.wireguardautotunnel.module.IoDispatcher
import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController import com.zaneschepke.wireguardautotunnel.ui.common.snackbar.SnackbarController
import com.zaneschepke.wireguardautotunnel.util.StringValue import com.zaneschepke.wireguardautotunnel.util.StringValue
@@ -23,7 +24,7 @@ class AutoTunnelViewModel
@Inject @Inject
constructor( constructor(
private val appDataRepository: AppDataRepository, private val appDataRepository: AppDataRepository,
private val rootShell: Provider<RootShell>, @AppShell private val rootShell: Provider<RootShell>,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher, @IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() { ) : ViewModel() {
@@ -21,7 +21,6 @@ object Constants {
const val SYSTEM_EXEMPT_SERVICE_TYPE_ID = 1024 const val SYSTEM_EXEMPT_SERVICE_TYPE_ID = 1024
const val SUBSCRIPTION_TIMEOUT = 5_000L const val SUBSCRIPTION_TIMEOUT = 5_000L
const val FOCUS_REQUEST_DELAY = 500L
const val TRANSITION_ANIMATION_TIME = 200 const val TRANSITION_ANIMATION_TIME = 200
@@ -4,8 +4,6 @@ import androidx.compose.ui.graphics.Color
import com.wireguard.android.util.RootShell import com.wireguard.android.util.RootShell
import com.wireguard.config.Peer import com.wireguard.config.Peer
import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus import com.zaneschepke.wireguardautotunnel.service.tunnel.HandshakeStatus
import com.zaneschepke.wireguardautotunnel.service.tunnel.TunnelState
import com.zaneschepke.wireguardautotunnel.service.tunnel.VpnState
import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics import com.zaneschepke.wireguardautotunnel.service.tunnel.statistics.TunnelStatistics
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
import com.zaneschepke.wireguardautotunnel.ui.theme.Straw import com.zaneschepke.wireguardautotunnel.ui.theme.Straw
@@ -23,10 +21,6 @@ fun TunnelStatistics.PeerStats.latestHandshakeSeconds(): Long? {
return NumberUtils.getSecondsBetweenTimestampAndNow(this.latestHandshakeEpochMillis) return NumberUtils.getSecondsBetweenTimestampAndNow(this.latestHandshakeEpochMillis)
} }
fun VpnState.isDown(): Boolean {
return this.status == TunnelState.DOWN
}
fun TunnelStatistics.PeerStats.handshakeStatus(): HandshakeStatus { fun TunnelStatistics.PeerStats.handshakeStatus(): HandshakeStatus {
// TODO add never connected status after duration // TODO add never connected status after duration
return this.latestHandshakeSeconds().let { return this.latestHandshakeSeconds().let {
+2
View File
@@ -225,4 +225,6 @@
<string name="kernel_not_supported">Kernel not supported</string> <string name="kernel_not_supported">Kernel not supported</string>
<string name="start_auto">Start auto-tunnel</string> <string name="start_auto">Start auto-tunnel</string>
<string name="stop_auto">Stop auto-tunnel</string> <string name="stop_auto">Stop auto-tunnel</string>
<string name="tunnel_running">Tunnel running</string>
<string name="monitoring_state_changes">Monitoring state changes</string>
</resources> </resources>
+1 -1
View File
@@ -1 +1 @@
2 4