diff options
| author | David Göransson <david.goransson@mullvad.net> | 2025-02-20 11:09:42 +0100 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2025-02-25 12:02:00 +0100 |
| commit | b63bc866946795be36a617adf65c8c6db071b05d (patch) | |
| tree | c93db6a693a01510653723812d0985c94f6cd910 /android | |
| parent | a473a917e1bfad3c7d9baa1a948eacb5096455aa (diff) | |
| download | mullvadvpn-b63bc866946795be36a617adf65c8c6db071b05d.tar.xz mullvadvpn-b63bc866946795be36a617adf65c8c6db071b05d.zip | |
Reduce open_tun calls (Establish)
Each call to Establish opens a window for leaks on android. By only
invoking Establish if the VpnConfig if any of the input has changed and
reusing it otherwise we avoid many of these leaks. This commit also
waits for android to report back that the routes have been created to
ping and verify connectivity to avoid pings going outside the tunnel.
Diffstat (limited to 'android')
3 files changed, 21 insertions, 28 deletions
diff --git a/android/app/src/test/kotlin/net/mullvad/talpid/TalpidVpnServiceFallbackDnsTest.kt b/android/app/src/test/kotlin/net/mullvad/talpid/TalpidVpnServiceFallbackDnsTest.kt index 27e7658a11..e3faaf3884 100644 --- a/android/app/src/test/kotlin/net/mullvad/talpid/TalpidVpnServiceFallbackDnsTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/talpid/TalpidVpnServiceFallbackDnsTest.kt @@ -34,6 +34,9 @@ class TalpidVpnServiceFallbackDnsTest { every { talpidVpnService.prepareVpnSafe() } returns Prepared.right() builderMockk = mockk<VpnService.Builder>() + every { talpidVpnService getProperty "connectivityListener" } returns + mockk<ConnectivityListener>(relaxed = true) + mockkConstructor(VpnService.Builder::class) every { anyConstructed<VpnService.Builder>().setMtu(any()) } returns builderMockk every { anyConstructed<VpnService.Builder>().setBlocking(any()) } returns builderMockk diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt index 9c82d62251..b702a39a6e 100644 --- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt +++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt @@ -10,6 +10,7 @@ import kotlin.collections.ArrayList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map @@ -18,7 +19,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.launch import net.mullvad.talpid.model.NetworkState import net.mullvad.talpid.util.NetworkEvent import net.mullvad.talpid.util.RawNetworkState @@ -31,29 +32,30 @@ class ConnectivityListener(private val connectivityManager: ConnectivityManager) val isConnected get() = _isConnected.value - private lateinit var _currentNetworkState: StateFlow<NetworkState?> + private val _mutableNetworkState = MutableStateFlow<NetworkState?>(null) private val resetNetworkState: Channel<Unit> = Channel() // Used by JNI val currentDefaultNetworkState: NetworkState? - get() = _currentNetworkState.value + get() = _mutableNetworkState.value // Used by JNI val currentDnsServers: ArrayList<InetAddress> - get() = _currentNetworkState.value?.dnsServers ?: ArrayList() + get() = _mutableNetworkState.value?.dnsServers ?: ArrayList() fun register(scope: CoroutineScope) { // Consider implementing retry logic for the flows below, because registering a listener on // the default network may fail if the network on Android 11 // https://issuetracker.google.com/issues/175055271?pli=1 - _currentNetworkState = + scope.launch { merge( connectivityManager.defaultRawNetworkStateFlow(), resetNetworkState.receiveAsFlow().map { null }, ) .map { it?.toNetworkState() } .onEach { notifyDefaultNetworkChange(it) } - .stateIn(scope, SharingStarted.Eagerly, null) + .collect(_mutableNetworkState) + } _isConnected = hasInternetCapability() @@ -70,8 +72,7 @@ class ConnectivityListener(private val connectivityManager: ConnectivityManager) * know the last known values not to be correct anymore. */ fun invalidateNetworkStateCache() { - // TODO remove runBlocking - runBlocking { resetNetworkState.send(Unit) } + _mutableNetworkState.value = null } private fun LinkProperties.dnsServersWithoutFallback(): List<InetAddress> = diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt index a143df6132..1457ff35f4 100644 --- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt +++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt @@ -57,34 +57,22 @@ open class TalpidVpnService : LifecycleVpnService() { // Used by JNI fun openTun(config: TunConfig): CreateTunResult = synchronized(this) { - val tunStatus = activeTunStatus - - if (config == currentTunConfig && tunStatus != null && tunStatus.isOpen) { - tunStatus - } else { - openTunImpl(config) + createTun(config).merge().also { + currentTunConfig = config + activeTunStatus = it } } // Used by JNI - fun openTunForced(config: TunConfig): CreateTunResult = - synchronized(this) { openTunImpl(config) } - - // Used by JNI - fun closeTun(): Unit = synchronized(this) { activeTunStatus = null } + fun closeTun(): Unit = + synchronized(this) { + connectivityListener.invalidateNetworkStateCache() + activeTunStatus = null + } // Used by JNI fun bypass(socket: Int): Boolean = protect(socket) - private fun openTunImpl(config: TunConfig): CreateTunResult { - val newTunStatus = createTun(config).merge() - - currentTunConfig = config - activeTunStatus = newTunStatus - - return newTunStatus - } - private fun createTun( config: TunConfig ): Either<CreateTunResult.Error, CreateTunResult.Success> = either { @@ -123,6 +111,7 @@ open class TalpidVpnService : LifecycleVpnService() { builder.addDnsServer(FALLBACK_DUMMY_DNS_SERVER) } + connectivityListener.invalidateNetworkStateCache() val vpnInterfaceFd = builder .establishSafe() |
