Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class MasterDnsVpnService : VpnService() {
private const val DEFAULT_SOCKS_PORT = 18000
private const val SOCKS_STARTUP_TIMEOUT_MS = 30 * 60 * 1000L
private const val SOCKS_POLL_INTERVAL_MS = 500L
private const val WAKE_LOCK_TIMEOUT_MS = SOCKS_STARTUP_TIMEOUT_MS + 5 * 60 * 1000L

// Base companions many apps need for network functionality.
private val BASE_COMPANION_PACKAGES = setOf(
Expand Down Expand Up @@ -90,6 +91,7 @@ class MasterDnsVpnService : VpnService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
ACTION_CONNECT -> {
startForeground(NOTIFICATION_ID, buildNotification(getString(R.string.notification_connecting)))
val profileId = intent.getLongExtra(EXTRA_PROFILE_ID, -1)
if (profileId > 0) {
startVpn(profileId)
Expand All @@ -111,8 +113,6 @@ class MasterDnsVpnService : VpnService() {
socksAuthWarningShown = false
sessionBusyWarningShown = false

// Show foreground notification
startForeground(NOTIFICATION_ID, buildNotification(getString(R.string.notification_connecting)))
acquireWakeLock()

// Load profile from DB
Expand Down Expand Up @@ -281,6 +281,7 @@ class MasterDnsVpnService : VpnService() {
.setMtu(1500)
.addAddress(if (globalSettings.fakeDnsEnabled) "172.19.0.1" else "10.0.0.2", if (globalSettings.fakeDnsEnabled) 30 else 32)
.addRoute("0.0.0.0", 0)
VpnManager.appendLog("IPv4 route enabled: 0.0.0.0/0. IPv6 is not routed by this Android VPN path.")
vpnDnsServers.forEach { builder.addDnsServer(it) }
if (globalSettings.fakeDnsEnabled) {
builder.addRoute("198.18.0.0", 16)
Expand Down Expand Up @@ -382,29 +383,39 @@ class MasterDnsVpnService : VpnService() {
serviceScope.launch {
try {
connectJob?.cancel()
VpnManager.appendLog("Stopping VPN...")
VpnManager.appendLog("VPN stop requested")

// Close TUN interface FIRST to unblock any pending I/O in Go tunBridge/tun
VpnManager.appendLog("Closing TUN interface...")
runCatching { vpnInterface?.close() }
.onSuccess { VpnManager.appendLog("TUN interface closed") }
.onFailure { VpnManager.appendLog("TUN interface close failed: ${it.message}") }
vpnInterface = null

if (tunBridgeActive) {
VpnManager.appendLog("Stopping DNS-aware TUN bridge...")
runCatching { mobile.Mobile.stopTunBridge() }
.onSuccess { VpnManager.appendLog("DNS-aware TUN bridge stopped") }
.onFailure { VpnManager.appendLog("DNS-aware TUN bridge stop failed: ${it.message}") }
tunBridgeActive = false
}

// Stop Go client and Tun bridge
VpnManager.appendLog("Stopping Go core...")
runCatching {
if (mobile.Mobile.isRunning()) {
mobile.Mobile.stopClient()
VpnManager.appendLog("Go core stop requested")
} else {
VpnManager.appendLog("Go core already stopped")
}
}.onFailure { e ->
Log.e(TAG, "Error stopping Go core", e)
VpnManager.appendLog("Go core stop failed: ${e.message}")
}

// Cancel coroutines
VpnManager.appendLog("Stopping Android session jobs...")
goClientJob?.cancel()
httpProxyJob?.cancel()
sharingSocksJob?.cancel()
Expand All @@ -413,6 +424,7 @@ class MasterDnsVpnService : VpnService() {
sharingSocksServer = null
runCatching { sharingHttpServer?.close() }
sharingHttpServer = null
VpnManager.appendLog("Android session jobs stopped")

VpnManager.updateState(VpnManager.VpnState.DISCONNECTED)
VpnManager.stopTrafficMonitor()
Expand All @@ -427,8 +439,10 @@ class MasterDnsVpnService : VpnService() {
@Suppress("DEPRECATION")
stopForeground(true)
}
VpnManager.appendLog("Foreground notification stopped")
}.onFailure {
Log.w(TAG, "Failed to stop foreground cleanly", it)
VpnManager.appendLog("Foreground notification stop failed: ${it.message}")
}

// Delay to allow UI to update before stopping service
Expand Down Expand Up @@ -712,7 +726,7 @@ class MasterDnsVpnService : VpnService() {
val pm = getSystemService(POWER_SERVICE) as? PowerManager ?: return
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "$TAG:runtime").apply {
setReferenceCounted(false)
acquire()
acquire(WAKE_LOCK_TIMEOUT_MS)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.masterdns.vpn.util
import android.content.Context
import android.content.Intent
import android.net.TrafficStats
import androidx.core.content.ContextCompat
import com.masterdns.vpn.data.local.ProfileEntity
import com.masterdns.vpn.service.MasterDnsVpnService
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -179,7 +180,7 @@ object VpnManager {
putExtra(MasterDnsVpnService.EXTRA_PROFILE_ID, profile.id)
}

runCatching { context.startService(intent) }
runCatching { ContextCompat.startForegroundService(context, intent) }
.onFailure {
setError("Failed to start VPN service: ${it.message}")
appendLog("Failed to start VPN service: ${it.message}")
Expand Down
Loading