diff options
| author | David Göransson <david.goransson@mullvad.net> | 2024-11-18 14:23:05 +0100 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2024-11-27 09:00:18 +0100 |
| commit | 1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988 (patch) | |
| tree | a83565926fb753a2dd9fd24f0e2bd07262e4507e /android/lib/common/src | |
| parent | 0d155385e1cb7075012bd270de0398d83a438bc5 (diff) | |
| download | mullvadvpn-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')
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() +} |
