fix: restart on boot, dynamic dns, auto tunnel reliability

Separate settings for starting tunnels vs auto tunnel on boot, fixing logic to make behavior more expected.

Fix a bug where dynamic DNS updater was only running once and not continually monitoring.

Further improvements to prevent spurious network monitoring states. Improved reevaluate job to allow for reliable manual tunnel overrides while auto tunnel is active.

Improved messaging on errors and dynamic dns events.
This commit is contained in:
Zane Schepke
2025-10-17 02:30:45 -04:00
parent 919816588b
commit 2cc71e657b
30 changed files with 810 additions and 192 deletions
@@ -17,13 +17,13 @@ import androidx.core.content.ContextCompat
import com.wireguard.android.util.RootShell
import com.zaneschepke.networkmonitor.shizuku.ShizukuShell
import com.zaneschepke.networkmonitor.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withTimeoutOrNull
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
class AndroidNetworkMonitor(
private val appContext: Context,
@@ -323,6 +323,13 @@ class AndroidNetworkMonitor(
.also { Timber.d("Current SSID via ${method.name}: $it") }
}
// prevent false positive late mobile data changes to combat android api quirks
private fun isLateCellularChange(previous: ConnectivityState, new: ConnectivityState): Boolean {
return (previous.wifiState.connected != new.wifiState.connected &&
previous.wifiState.ssid == new.wifiState.ssid &&
previous.cellularConnected != new.cellularConnected)
}
override val connectivityStateFlow: SharedFlow<ConnectivityState> =
combine(
wifiFlow.scan(
@@ -377,6 +384,22 @@ class AndroidNetworkMonitor(
)
.also { Timber.d("Connectivity Status: $it") }
}
.scan(
ConnectivityState(
WifiState(
locationPermissionsGranted = hasRequiredLocationPermissions(),
locationServicesEnabled =
locationManager?.isLocationServicesEnabled() ?: false,
)
)
) { previous, current ->
if (isLateCellularChange(previous, current)) {
Timber.d("Skipping late cellular change")
previous
} else {
current
}
}
.distinctUntilChanged()
.shareIn(applicationScope, SharingStarted.Eagerly, replay = 1)
@@ -1,6 +1,10 @@
package com.zaneschepke.networkmonitor.shizuku
import android.os.ParcelFileDescriptor
import java.io.BufferedReader
import java.io.FileInputStream
import java.io.InputStreamReader
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -8,10 +12,6 @@ import moe.shizuku.server.IRemoteProcess
import moe.shizuku.server.IShizukuService
import rikka.shizuku.Shizuku
import timber.log.Timber
import java.io.BufferedReader
import java.io.FileInputStream
import java.io.InputStreamReader
import kotlin.coroutines.resumeWithException
class ShizukuShell(private val applicationScope: CoroutineScope) {
interface CommandResultListener {