summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
Diffstat (limited to 'android')
-rw-r--r--android/CHANGELOG.md2
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/talpid/util/ConnectivityManagerUtilKtTest.kt32
-rw-r--r--android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt4
-rw-r--r--android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/Connectivity.kt24
-rw-r--r--android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/IpAvailability.kt7
-rw-r--r--android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/ConnectivityManagerUtil.kt11
-rw-r--r--android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/UnderlyingConnectivityStatusResolver.kt4
7 files changed, 55 insertions, 29 deletions
diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md
index 2bbb8e5f91..9d7c4963a8 100644
--- a/android/CHANGELOG.md
+++ b/android/CHANGELOG.md
@@ -37,7 +37,7 @@ Line wrap the file at 100 chars. Th
- Remove Google's resolvers from encrypted DNS proxy.
### Fixed
-- Will no longer try to connect over IPv6 if IPv6 is not available.
+- Will no longer try to connect using an IP version if that IP version is not available.
## [android/2025.1-beta1] - 2025-03-05
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/talpid/util/ConnectivityManagerUtilKtTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/talpid/util/ConnectivityManagerUtilKtTest.kt
index 354b6f585d..39df5de05b 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/talpid/util/ConnectivityManagerUtilKtTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/talpid/util/ConnectivityManagerUtilKtTest.kt
@@ -20,6 +20,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.test.runTest
import net.mullvad.talpid.model.Connectivity
+import net.mullvad.talpid.model.IpAvailability
import net.mullvad.talpid.util.NetworkEvent
import net.mullvad.talpid.util.UnderlyingConnectivityStatusResolver
import net.mullvad.talpid.util.defaultNetworkEvents
@@ -53,7 +54,7 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// Since initial state and listener both return `true` within debounce we only see one
// event
- assertEquals(Connectivity.Status(true, true), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4AndIpv6), awaitItem())
expectNoEvents()
}
}
@@ -66,7 +67,7 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// Initially offline and no network events, so we should get a single `false` event
- assertEquals(Connectivity.Status(false, false), awaitItem())
+ assertEquals(Connectivity.Offline, awaitItem())
expectNoEvents()
}
}
@@ -88,8 +89,8 @@ class ConnectivityManagerUtilKtTest {
}
connectivityManager.hasInternetConnectivity(mockResolver).test {
- assertEquals(Connectivity.Status(false, false), awaitItem())
- assertEquals(Connectivity.Status(true, true), awaitItem())
+ assertEquals(Connectivity.Offline, awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4AndIpv6), awaitItem())
expectNoEvents()
}
}
@@ -113,8 +114,8 @@ class ConnectivityManagerUtilKtTest {
}
connectivityManager.hasInternetConnectivity(mockResolver).test {
- assertEquals(Connectivity.Status(true, true), awaitItem())
- assertEquals(Connectivity.Status(false, false), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4AndIpv6), awaitItem())
+ assertEquals(Connectivity.Offline, awaitItem())
expectNoEvents()
}
}
@@ -150,7 +151,7 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// We should always only see us being online
- assertEquals(Connectivity.Status(ipv4 = true, ipv6 = false), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4), awaitItem())
expectNoEvents()
}
}
@@ -184,7 +185,7 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// We should always only see us being online, small offline state is caught by debounce
- assertEquals(Connectivity.Status(ipv4 = true, ipv6 = false), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4), awaitItem())
expectNoEvents()
}
}
@@ -218,11 +219,11 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// Wifi is online
- assertEquals(Connectivity.Status(false, true), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv6), awaitItem())
// We didn't get any network within debounce time, so we are offline
- assertEquals(Connectivity.Status(false, false), awaitItem())
+ assertEquals(Connectivity.Offline, awaitItem())
// Cellular network is online
- assertEquals(Connectivity.Status(false, true), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv6), awaitItem())
expectNoEvents()
}
}
@@ -250,9 +251,9 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// Ipv6 network is online
- assertEquals(Connectivity.Status(false, true), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv6), awaitItem())
// Ipv4 network is online
- assertEquals(Connectivity.Status(true, false), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4), awaitItem())
expectNoEvents()
}
}
@@ -265,7 +266,8 @@ class ConnectivityManagerUtilKtTest {
every { capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) } returns
false
val mockResolver = mockk<UnderlyingConnectivityStatusResolver>()
- every { mockResolver.currentStatus() } returns Connectivity.Status(true, true)
+ every { mockResolver.currentStatus() } returns
+ Connectivity.Online(IpAvailability.Ipv4AndIpv6)
every { connectivityManager.defaultNetworkEvents() } returns
callbackFlow {
@@ -276,7 +278,7 @@ class ConnectivityManagerUtilKtTest {
connectivityManager.hasInternetConnectivity(mockResolver).test {
// Network is online
- assertEquals(Connectivity.Status(true, true), awaitItem())
+ assertEquals(Connectivity.Online(IpAvailability.Ipv4AndIpv6), awaitItem())
}
verify(exactly = 1) { mockResolver.currentStatus() }
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 ede883a837..460dac8f68 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
@@ -66,7 +66,7 @@ class ConnectivityListener(
_isConnected =
connectivityManager
.hasInternetConnectivity(resolver)
- .onEach { notifyConnectivityChange(it.ipv4, it.ipv6) }
+ .onEach { notifyConnectivityChange(it) }
.stateIn(
scope + Dispatchers.IO,
SharingStarted.Eagerly,
@@ -99,7 +99,7 @@ class ConnectivityListener(
linkProperties?.dnsServersWithoutFallback(),
)
- private external fun notifyConnectivityChange(isIPv4: Boolean, isIPv6: Boolean)
+ private external fun notifyConnectivityChange(connectivity: Connectivity)
private external fun notifyDefaultNetworkChange(networkState: NetworkState?)
}
diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/Connectivity.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/Connectivity.kt
index b87eaaacc8..46195636bc 100644
--- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/Connectivity.kt
+++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/Connectivity.kt
@@ -1,8 +1,30 @@
package net.mullvad.talpid.model
+import android.net.LinkAddress
+import java.net.Inet4Address
+import java.net.Inet6Address
+
sealed class Connectivity {
- data class Status(val ipv4: Boolean, val ipv6: Boolean) : Connectivity()
+ data class Online(val ipAvailability: IpAvailability) : Connectivity()
+
+ data object Offline : Connectivity()
// Required by jni
data object PresumeOnline : Connectivity()
+
+ companion object {
+ fun fromIpAvailability(ipv4: Boolean, ipv6: Boolean) =
+ when {
+ ipv4 && ipv6 -> Online(IpAvailability.Ipv4AndIpv6)
+ ipv4 -> Online(IpAvailability.Ipv4)
+ ipv6 -> Online(IpAvailability.Ipv6)
+ else -> Offline
+ }
+
+ fun fromLinkAddresses(linkAddresses: List<LinkAddress>): Connectivity {
+ val ipv4 = linkAddresses.any { it.address is Inet4Address }
+ val ipv6 = linkAddresses.any { it.address is Inet6Address }
+ return fromIpAvailability(ipv4, ipv6)
+ }
+ }
}
diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/IpAvailability.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/IpAvailability.kt
new file mode 100644
index 0000000000..916f9d0a88
--- /dev/null
+++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/IpAvailability.kt
@@ -0,0 +1,7 @@
+package net.mullvad.talpid.model
+
+enum class IpAvailability {
+ Ipv4,
+ Ipv6,
+ Ipv4AndIpv6,
+}
diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/ConnectivityManagerUtil.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/ConnectivityManagerUtil.kt
index 7a0208eaa1..5409bc2f99 100644
--- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/ConnectivityManagerUtil.kt
+++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/ConnectivityManagerUtil.kt
@@ -6,8 +6,6 @@ import android.net.LinkProperties
import android.net.Network
import android.net.NetworkCapabilities
import co.touchlab.kermit.Logger
-import java.net.Inet4Address
-import java.net.Inet6Address
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.awaitClose
@@ -137,7 +135,7 @@ internal fun ConnectivityManager.activeRawNetworkState(): RawNetworkState? =
@OptIn(FlowPreview::class)
fun ConnectivityManager.hasInternetConnectivity(
resolver: UnderlyingConnectivityStatusResolver
-): Flow<Connectivity.Status> =
+): Flow<Connectivity> =
this.defaultRawNetworkStateFlow()
.debounce(CONNECTIVITY_DEBOUNCE)
.map { resolveConnectivityStatus(it, resolver) }
@@ -146,7 +144,7 @@ fun ConnectivityManager.hasInternetConnectivity(
internal fun resolveConnectivityStatus(
currentRawNetworkState: RawNetworkState?,
resolver: UnderlyingConnectivityStatusResolver,
-): Connectivity.Status =
+): Connectivity =
if (currentRawNetworkState.isVpn()) {
// If the default network is a VPN we need to use a socket to check
// the underlying network
@@ -158,10 +156,7 @@ internal fun resolveConnectivityStatus(
}
private fun RawNetworkState?.toConnectivityStatus() =
- Connectivity.Status(
- ipv4 = this?.linkProperties?.linkAddresses?.any { it.address is Inet4Address } == true,
- ipv6 = this?.linkProperties?.linkAddresses?.any { it.address is Inet6Address } == true,
- )
+ Connectivity.fromLinkAddresses(this?.linkProperties?.linkAddresses ?: emptyList())
private fun RawNetworkState?.isVpn(): Boolean =
this?.networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) == false
diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/UnderlyingConnectivityStatusResolver.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/UnderlyingConnectivityStatusResolver.kt
index 0024f63357..1a5cb33be4 100644
--- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/UnderlyingConnectivityStatusResolver.kt
+++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/UnderlyingConnectivityStatusResolver.kt
@@ -14,8 +14,8 @@ import net.mullvad.talpid.model.Connectivity
class UnderlyingConnectivityStatusResolver(
private val protect: (socket: DatagramSocket) -> Boolean
) {
- fun currentStatus(): Connectivity.Status =
- Connectivity.Status(ipv4 = hasIpv4(), ipv6 = hasIpv6())
+ fun currentStatus(): Connectivity =
+ Connectivity.fromIpAvailability(ipv4 = hasIpv4(), ipv6 = hasIpv6())
private fun hasIpv4(): Boolean =
hasIpVersion(Inet4Address.getByName(PUBLIC_IPV4_ADDRESS), protect)