summaryrefslogtreecommitdiffhomepage
path: root/android/lib/common/src
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2024-11-18 14:23:05 +0100
committerDavid Göransson <david.goransson@mullvad.net>2024-11-27 09:00:18 +0100
commit1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988 (patch)
treea83565926fb753a2dd9fd24f0e2bd07262e4507e /android/lib/common/src
parent0d155385e1cb7075012bd270de0398d83a438bc5 (diff)
downloadmullvadvpn-1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988.tar.xz
mullvadvpn-1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988.zip
Handle legacy always-on vpn profiles
Co-authored-by: Jonatan Rhodin <jonatan.rhodin@mullvad.net>
Diffstat (limited to 'android/lib/common/src')
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt2
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt13
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorNotificationMessage.kt7
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt77
-rw-r--r--android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/VpnServiceUtils.kt61
5 files changed, 62 insertions, 98 deletions
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt
index ea420f2d0a..76f71d82e3 100644
--- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt
+++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt
@@ -3,4 +3,4 @@ package net.mullvad.mullvadvpn.lib.common.constant
// Actions
const val KEY_CONNECT_ACTION = "$MULLVAD_PACKAGE_NAME.connect_action"
const val KEY_DISCONNECT_ACTION = "$MULLVAD_PACKAGE_NAME.disconnect_action"
-const val KEY_REQUEST_VPN_PERMISSION = "$MULLVAD_PACKAGE_NAME.request_vpn_permission"
+const val KEY_REQUEST_VPN_PROFILE = "$MULLVAD_PACKAGE_NAME.request_vpn_profile"
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt
index 7ea74edfaa..992ae9404d 100644
--- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt
+++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt
@@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings
-import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.getInstalledPackagesList
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
private const val ALWAYS_ON_VPN_APP = "always_on_vpn_app"
@@ -20,18 +19,6 @@ fun createAccountUri(accountUri: String, websiteAuthToken: WebsiteAuthToken?): U
return Uri.parse(urlString)
}
-fun Context.getAlwaysOnVpnAppName(): String? {
- return resolveAlwaysOnVpnPackageName()
- ?.let { currentAlwaysOnVpn ->
- packageManager.getInstalledPackagesList(0).singleOrNull {
- it.packageName == currentAlwaysOnVpn && it.packageName != packageName
- }
- }
- ?.applicationInfo
- ?.loadLabel(packageManager)
- ?.toString()
-}
-
// NOTE: This function will return the current Always-on VPN package's name. In case of either
// Always-on VPN being disabled or not being able to read the state, NULL will be returned.
fun Context.resolveAlwaysOnVpnPackageName(): String? {
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorNotificationMessage.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorNotificationMessage.kt
deleted file mode 100644
index 4a5c902d96..0000000000
--- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorNotificationMessage.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.mullvad.mullvadvpn.lib.common.util
-
-data class ErrorNotificationMessage(
- val titleResourceId: Int,
- val messageResourceId: Int,
- val optionalMessageArgument: String? = null,
-)
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt
deleted file mode 100644
index a61ec10c17..0000000000
--- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package net.mullvad.mullvadvpn.lib.common.util
-
-import android.content.Context
-import net.mullvad.mullvadvpn.lib.common.R
-import net.mullvad.mullvadvpn.lib.model.AuthFailedError
-import net.mullvad.mullvadvpn.lib.model.ErrorState
-import net.mullvad.mullvadvpn.lib.model.ErrorStateCause
-import net.mullvad.mullvadvpn.lib.model.ParameterGenerationError
-import net.mullvad.talpid.util.addressString
-
-fun ErrorState.getErrorNotificationResources(context: Context): ErrorNotificationMessage {
- return when {
- cause is ErrorStateCause.InvalidDnsServers -> {
- ErrorNotificationMessage(
- R.string.blocking_internet,
- cause.errorMessageId(),
- (cause as ErrorStateCause.InvalidDnsServers).addresses.joinToString { address ->
- address.addressString()
- },
- )
- }
- cause is ErrorStateCause.VpnPermissionDenied -> {
- resolveAlwaysOnVpnErrorNotificationMessage(context.getAlwaysOnVpnAppName())
- }
- isBlocking -> ErrorNotificationMessage(R.string.blocking_internet, cause.errorMessageId())
- else -> ErrorNotificationMessage(R.string.critical_error, R.string.failed_to_block_internet)
- }
-}
-
-private fun resolveAlwaysOnVpnErrorNotificationMessage(
- alwaysOnVpnAppName: String?
-): ErrorNotificationMessage {
- return if (alwaysOnVpnAppName != null) {
- ErrorNotificationMessage(
- R.string.always_on_vpn_error_notification_title,
- R.string.always_on_vpn_error_notification_content,
- alwaysOnVpnAppName,
- )
- } else {
- ErrorNotificationMessage(
- R.string.vpn_permission_error_notification_title,
- R.string.vpn_permission_error_notification_message,
- )
- }
-}
-
-fun ErrorStateCause.errorMessageId(): Int =
- when (this) {
- is ErrorStateCause.InvalidDnsServers -> R.string.invalid_dns_servers
- is ErrorStateCause.AuthFailed -> error.errorMessageId()
- is ErrorStateCause.Ipv6Unavailable -> R.string.ipv6_unavailable
- is ErrorStateCause.FirewallPolicyError -> R.string.set_firewall_policy_error
- is ErrorStateCause.DnsError -> R.string.set_dns_error
- is ErrorStateCause.StartTunnelError -> R.string.start_tunnel_error
- is ErrorStateCause.IsOffline -> R.string.is_offline
- is ErrorStateCause.TunnelParameterError -> {
- when (error) {
- ParameterGenerationError.NoMatchingRelay,
- ParameterGenerationError.NoMatchingBridgeRelay -> {
- R.string.no_matching_relay
- }
- ParameterGenerationError.NoWireguardKey -> R.string.no_wireguard_key
- ParameterGenerationError.CustomTunnelHostResultionError -> {
- R.string.custom_tunnel_host_resolution_error
- }
- }
- }
- is ErrorStateCause.VpnPermissionDenied -> R.string.vpn_permission_denied_error
- }
-
-fun AuthFailedError.errorMessageId(): Int =
- when (this) {
- AuthFailedError.ExpiredAccount -> R.string.account_credit_has_expired
- AuthFailedError.InvalidAccount,
- AuthFailedError.TooManyConnections,
- AuthFailedError.Unknown -> R.string.auth_failed
- }
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/VpnServiceUtils.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/VpnServiceUtils.kt
new file mode 100644
index 0000000000..59833cb396
--- /dev/null
+++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/VpnServiceUtils.kt
@@ -0,0 +1,61 @@
+package net.mullvad.mullvadvpn.lib.common.util
+
+import android.content.Context
+import android.content.Intent
+import android.net.VpnService.prepare
+import arrow.core.Either
+import arrow.core.flatten
+import arrow.core.left
+import arrow.core.right
+import co.touchlab.kermit.Logger
+import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.getInstalledPackagesList
+import net.mullvad.mullvadvpn.lib.model.PrepareError
+import net.mullvad.mullvadvpn.lib.model.Prepared
+
+/**
+ * Invoking VpnService.prepare() can result in 3 out comes:
+ * 1. IllegalStateException - There is a legacy VPN profile marked as always on
+ * 2. Intent
+ * - A: Can-prepare - Create Vpn profile
+ * - B: Always-on-VPN - Another Vpn Profile is marked as always on
+ * 3. null - The app has the VPN permission
+ *
+ * In case 1 and 2b, you don't know if you have a VPN profile or not.
+ */
+fun Context.prepareVpnSafe(): Either<PrepareError, Prepared> =
+ Either.catch {
+ val intent: Intent? = prepare(this)
+ intent
+ }
+ .mapLeft {
+ Logger.e("VpnService.prepare() failed: $it")
+ when (it) {
+ is IllegalStateException -> PrepareError.OtherLegacyAlwaysOnVpn
+ else -> throw it
+ }
+ }
+ .map { intent ->
+ if (intent == null) {
+ Prepared.right()
+ } else {
+ val alwaysOnVpnApp = getAlwaysOnVpnAppName()
+ if (alwaysOnVpnApp == null) {
+ PrepareError.NotPrepared(intent).left()
+ } else {
+ PrepareError.OtherAlwaysOnApp(alwaysOnVpnApp).left()
+ }
+ }
+ }
+ .flatten()
+
+fun Context.getAlwaysOnVpnAppName(): String? {
+ return resolveAlwaysOnVpnPackageName()
+ ?.let { currentAlwaysOnVpn ->
+ packageManager.getInstalledPackagesList(0).singleOrNull {
+ it.packageName == currentAlwaysOnVpn && it.packageName != packageName
+ }
+ }
+ ?.applicationInfo
+ ?.loadLabel(packageManager)
+ ?.toString()
+}