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 | |
| 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')
59 files changed, 518 insertions, 440 deletions
diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 37562f4119..cae6231483 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -35,6 +35,7 @@ Line wrap the file at 100 chars. Th - Fix a bug where the Android account expiry notifications would not be updated if the app was running in the background for a long time. - Fix ANR due to the tokio runtime being blocked by `getaddrinfo` when dropped. +- Fix crash when having a legacy VPN profile as always-on. ## [android/2024.8] - 2024-11-01 diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt index 487e739025..9087fe2f6c 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt @@ -241,7 +241,7 @@ class ConnectScreenTest { } // Assert - onNodeWithText("FAILED TO SECURE CONNECTION").assertExists() + onNodeWithText("FAILED TO CONNECT").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Dismiss").assertExists() onNodeWithText(text = "Critical error (your attention is required)", ignoreCase = true) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt index 538a746b99..bc1aaeb641 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationBanner.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.toUpperCase import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout @@ -129,7 +130,7 @@ private fun Notification(notificationBannerData: NotificationData) { }, ) Text( - text = title.uppercase(), + text = title.toUpperCase(), modifier = Modifier.constrainAs(textTitle) { top.linkTo(parent.top) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationData.kt index 5318933852..97a7b9a483 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationData.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/notificationbanner/NotificationData.kt @@ -3,7 +3,6 @@ package net.mullvad.mullvadvpn.compose.component.notificationbanner import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.OpenInNew import androidx.compose.material.icons.filled.Clear -import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector @@ -13,26 +12,29 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.FontWeight import androidx.core.text.HtmlCompat +import java.net.InetAddress import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.extensions.getExpiryQuantityString import net.mullvad.mullvadvpn.compose.extensions.toAnnotatedString -import net.mullvad.mullvadvpn.lib.common.util.getErrorNotificationResources +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.mullvadvpn.repository.InAppNotification import net.mullvad.mullvadvpn.ui.notification.StatusLevel data class NotificationData( - val title: String, + val title: AnnotatedString, val message: AnnotatedString? = null, val statusLevel: StatusLevel, val action: NotificationAction? = null, ) { constructor( title: String, - message: String?, + message: String? = null, statusLevel: StatusLevel, - action: NotificationAction?, - ) : this(title, message?.let { AnnotatedString(it) }, statusLevel, action) + action: NotificationAction? = null, + ) : this(AnnotatedString(title), message?.let { AnnotatedString(it) }, statusLevel, action) } data class NotificationAction( @@ -51,22 +53,11 @@ fun InAppNotification.toNotificationData( when (this) { is InAppNotification.NewDevice -> NotificationData( - title = stringResource(id = R.string.new_device_notification_title), + title = + AnnotatedString(stringResource(id = R.string.new_device_notification_title)), message = - HtmlCompat.fromHtml( - stringResource( - id = R.string.new_device_notification_message, - deviceName, - ), - HtmlCompat.FROM_HTML_MODE_COMPACT, - ) - .toAnnotatedString( - boldSpanStyle = - SpanStyle( - color = MaterialTheme.colorScheme.onSurface, - fontWeight = FontWeight.ExtraBold, - ) - ), + stringResource(id = R.string.new_device_notification_message, deviceName) + .formatWithHtml(), statusLevel = StatusLevel.Info, action = NotificationAction( @@ -111,23 +102,94 @@ fun InAppNotification.toNotificationData( @Composable private fun errorMessageBannerData(error: ErrorState) = - error.getErrorNotificationResources(LocalContext.current).run { - NotificationData( - title = stringResource(id = titleResourceId), - message = - HtmlCompat.fromHtml( - optionalMessageArgument?.let { stringResource(id = messageResourceId, it) } - ?: stringResource(id = messageResourceId), - HtmlCompat.FROM_HTML_MODE_COMPACT, - ) - .toAnnotatedString( - boldSpanStyle = - SpanStyle( - color = MaterialTheme.colorScheme.onSurface, - fontWeight = FontWeight.ExtraBold, - ) - ), - statusLevel = StatusLevel.Error, - action = null, + NotificationData( + title = error.title().formatWithHtml(), + message = error.message().formatWithHtml(), + statusLevel = StatusLevel.Error, + action = null, + ) + +@Composable +private fun String.formatWithHtml(): AnnotatedString = + HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_COMPACT) + .toAnnotatedString( + boldSpanStyle = + SpanStyle( + color = MaterialTheme.colorScheme.onSurface, + fontWeight = FontWeight.ExtraBold, + ) ) + +@Composable +private fun ErrorState.title(): String { + val cause = this.cause + return when { + cause is ErrorStateCause.InvalidDnsServers -> stringResource(R.string.blocking_internet) + cause is ErrorStateCause.NotPrepared -> + stringResource(R.string.vpn_permission_error_notification_title) + cause is ErrorStateCause.OtherAlwaysOnApp -> + stringResource(R.string.always_on_vpn_error_notification_title, cause.appName) + cause is ErrorStateCause.OtherLegacyAlwaysOnApp -> + stringResource(R.string.legacy_always_on_vpn_error_notification_title) + isBlocking -> stringResource(R.string.blocking_internet) + else -> stringResource(R.string.critical_error) } +} + +@Composable +private fun ErrorState.message(): String { + val cause = this.cause + return when { + isBlocking -> cause.errorMessageId() + else -> stringResource(R.string.failed_to_block_internet) + } +} + +@Composable +private fun ErrorStateCause.errorMessageId(): String = + when (this) { + is ErrorStateCause.AuthFailed -> stringResource(error.errorMessageId()) + is ErrorStateCause.Ipv6Unavailable -> stringResource(R.string.ipv6_unavailable) + is ErrorStateCause.FirewallPolicyError -> stringResource(R.string.set_firewall_policy_error) + is ErrorStateCause.DnsError -> stringResource(R.string.set_dns_error) + is ErrorStateCause.StartTunnelError -> stringResource(R.string.start_tunnel_error) + is ErrorStateCause.IsOffline -> stringResource(R.string.is_offline) + is ErrorStateCause.TunnelParameterError -> stringResource(error.errorMessageId()) + is ErrorStateCause.NotPrepared -> + stringResource(R.string.vpn_permission_error_notification_message) + is ErrorStateCause.OtherAlwaysOnApp -> + stringResource(R.string.always_on_vpn_error_notification_content, appName) + is ErrorStateCause.OtherLegacyAlwaysOnApp -> + stringResource(R.string.legacy_always_on_vpn_error_notification_content) + is ErrorStateCause.InvalidDnsServers -> + stringResource( + R.string.invalid_dns_servers, + addresses.joinToString { address -> address.addressString() }, + ) + } + +private fun AuthFailedError.errorMessageId(): Int = + when (this) { + AuthFailedError.ExpiredAccount -> R.string.account_credit_has_expired + AuthFailedError.InvalidAccount, + AuthFailedError.TooManyConnections, + AuthFailedError.Unknown -> R.string.auth_failed + } + +private fun ParameterGenerationError.errorMessageId(): Int = + when (this) { + 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 + } + +private fun InetAddress.addressString(): String { + val hostNameAndAddress = this.toString().split('/', limit = 2) + val address = hostNameAndAddress[1] + + return address +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index c3640979d3..b143e72f25 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -61,7 +61,6 @@ import com.ramcosta.composedestinations.generated.destinations.SettingsDestinati import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -import mullvad_daemon.management_interface.tunnelState import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.button.ConnectionButton import net.mullvad.mullvadvpn.compose.button.SwitchLocationButton @@ -84,8 +83,8 @@ import net.mullvad.mullvadvpn.compose.test.RECONNECT_BUTTON_TEST_TAG import net.mullvad.mullvadvpn.compose.test.SELECT_LOCATION_BUTTON_TEST_TAG import net.mullvad.mullvadvpn.compose.transitions.HomeTransition import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle +import net.mullvad.mullvadvpn.compose.util.CreateVpnProfile import net.mullvad.mullvadvpn.compose.util.OnNavResultValue -import net.mullvad.mullvadvpn.compose.util.RequestVpnPermission import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately import net.mullvad.mullvadvpn.constant.SECURE_ZOOM import net.mullvad.mullvadvpn.constant.SECURE_ZOOM_ANIMATION_MILLIS @@ -100,6 +99,7 @@ import net.mullvad.mullvadvpn.lib.model.GeoIpLocation import net.mullvad.mullvadvpn.lib.model.LatLong import net.mullvad.mullvadvpn.lib.model.Latitude import net.mullvad.mullvadvpn.lib.model.Longitude +import net.mullvad.mullvadvpn.lib.model.PrepareError import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens @@ -142,9 +142,9 @@ fun Connect( val snackbarHostState = remember { SnackbarHostState() } - val launchVpnPermission = - rememberLauncherForActivityResult(RequestVpnPermission()) { - connectViewModel.requestVpnPermissionResult(it) + val createVpnProfile = + rememberLauncherForActivityResult(CreateVpnProfile()) { + connectViewModel.createVpnProfileResult(it) } val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook() @@ -154,9 +154,8 @@ fun Connect( minActiveState = Lifecycle.State.RESUMED, ) { sideEffect -> when (sideEffect) { - is ConnectViewModel.UiSideEffect.OpenAccountManagementPageInBrowser -> { + is ConnectViewModel.UiSideEffect.OpenAccountManagementPageInBrowser -> openAccountPage(sideEffect.token) - } is ConnectViewModel.UiSideEffect.OutOfTime -> navigator.navigate(OutOfTimeDestination) { @@ -170,7 +169,24 @@ fun Connect( popUpTo(NavGraphs.root) { inclusive = true } } - is ConnectViewModel.UiSideEffect.NoVpnPermission -> launchVpnPermission.launch(Unit) + is ConnectViewModel.UiSideEffect.NotPrepared -> + when (sideEffect.prepareError) { + is PrepareError.OtherLegacyAlwaysOnVpn -> + launch { + snackbarHostState.showSnackbarImmediately( + message = sideEffect.prepareError.toMessage(context) + ) + } + + is PrepareError.OtherAlwaysOnApp -> + launch { + snackbarHostState.showSnackbarImmediately( + message = sideEffect.prepareError.toMessage(context) + ) + } + is PrepareError.NotPrepared -> + createVpnProfile.launch(sideEffect.prepareError.prepareIntent) + } is ConnectViewModel.UiSideEffect.ConnectError -> launch { snackbarHostState.showSnackbarImmediately( @@ -178,13 +194,12 @@ fun Connect( ) } - is ConnectViewModel.UiSideEffect.OpenUri -> { + is ConnectViewModel.UiSideEffect.OpenUri -> try { uriHandler.openUri(sideEffect.uri.toString()) } catch (e: IllegalArgumentException) { Logger.w("Failed to open uri", e) } - } } } @@ -581,15 +596,17 @@ fun GeoIpLocation.toLatLong() = private fun ConnectViewModel.UiSideEffect.ConnectError.toMessage(context: Context): String = when (this) { - ConnectViewModel.UiSideEffect.ConnectError.NoVpnPermission -> - context.getString(R.string.vpn_permission_denied_error) - - is ConnectViewModel.UiSideEffect.ConnectError.AlwaysOnVpn -> - // Snackbar currently do not support annotated string - context - .getString(R.string.always_on_vpn_error_notification_content, appName) - .removeHtmlTags() - ConnectViewModel.UiSideEffect.ConnectError.Generic -> context.getString(R.string.error_occurred) + + ConnectViewModel.UiSideEffect.ConnectError.PermissionDenied -> + context.getString(R.string.vpn_permission_denied_error) } + +private fun PrepareError.OtherLegacyAlwaysOnVpn.toMessage(context: Context) = + context + .getString(R.string.always_on_vpn_error_notification_content, "Legacy app") + .removeHtmlTags() + +private fun PrepareError.OtherAlwaysOnApp.toMessage(context: Context) = + context.getString(R.string.always_on_vpn_error_notification_content, appName).removeHtmlTags() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt index 332992c4e5..f52a4e8879 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt @@ -7,9 +7,11 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.navigation.NavHostController +import arrow.core.merge import co.touchlab.kermit.Logger import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.generated.NavGraphs @@ -17,11 +19,14 @@ import com.ramcosta.composedestinations.generated.destinations.NoDaemonDestinati import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.rememberNavHostEngine import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator -import net.mullvad.mullvadvpn.compose.util.RequestVpnPermission +import net.mullvad.mullvadvpn.compose.util.CreateVpnProfile +import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe +import net.mullvad.mullvadvpn.lib.model.PrepareError +import net.mullvad.mullvadvpn.lib.model.Prepared import net.mullvad.mullvadvpn.viewmodel.DaemonScreenEvent import net.mullvad.mullvadvpn.viewmodel.NoDaemonViewModel -import net.mullvad.mullvadvpn.viewmodel.VpnPermissionSideEffect -import net.mullvad.mullvadvpn.viewmodel.VpnPermissionViewModel +import net.mullvad.mullvadvpn.viewmodel.VpnProfileSideEffect +import net.mullvad.mullvadvpn.viewmodel.VpnProfileViewModel import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalComposeUiApi::class) @@ -32,7 +37,7 @@ fun MullvadApp() { val navigator: DestinationsNavigator = navHostController.rememberDestinationsNavigator() val serviceVm = koinViewModel<NoDaemonViewModel>() - val permissionVm = koinViewModel<VpnPermissionViewModel>() + val permissionVm = koinViewModel<VpnProfileViewModel>() DisposableEffect(Unit) { navHostController.addOnDestinationChangedListener(serviceVm) @@ -64,11 +69,20 @@ fun MullvadApp() { // Ask for VPN Permission val launchVpnPermission = - rememberLauncherForActivityResult(RequestVpnPermission()) { _ -> permissionVm.connect() } + rememberLauncherForActivityResult(CreateVpnProfile()) { _ -> permissionVm.connect() } + val context = LocalContext.current LaunchedEffect(Unit) { permissionVm.uiSideEffect.collect { - if (it is VpnPermissionSideEffect.ShowDialog) { - launchVpnPermission.launch(Unit) + if (it is VpnProfileSideEffect.RequestVpnProfile) { + val prepareResult = context.prepareVpnSafe().merge() + when (prepareResult) { + is PrepareError.NotPrepared -> + launchVpnPermission.launch(prepareResult.prepareIntent) + // If legacy or other always on connect at let daemon generate a error state + is PrepareError.OtherLegacyAlwaysOnVpn, + is PrepareError.OtherAlwaysOnApp, + Prepared -> permissionVm.connect() + } } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/CreateVpnProfile.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/CreateVpnProfile.kt new file mode 100644 index 0000000000..750ca6485c --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/CreateVpnProfile.kt @@ -0,0 +1,14 @@ +package net.mullvad.mullvadvpn.compose.util + +import android.app.Activity +import android.content.Context +import android.content.Intent +import androidx.activity.result.contract.ActivityResultContract + +class CreateVpnProfile : ActivityResultContract<Intent, Boolean>() { + override fun createIntent(context: Context, input: Intent): Intent = input + + override fun parseResult(resultCode: Int, intent: Intent?): Boolean { + return resultCode == Activity.RESULT_OK + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/RequestVpnPermission.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/RequestVpnPermission.kt deleted file mode 100644 index f198a3159c..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/RequestVpnPermission.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.mullvad.mullvadvpn.compose.util - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.VpnService -import androidx.activity.result.contract.ActivityResultContract - -class RequestVpnPermission : ActivityResultContract<Unit, Boolean>() { - override fun createIntent(context: Context, input: Unit): Intent { - return VpnService.prepare(context)!! - } - - override fun parseResult(resultCode: Int, intent: Intent?): Boolean { - return resultCode == Activity.RESULT_OK - } - - // We expect this permission to only be requested when the permission is missing. However, - // if it for some reason is called incorrectly we will skip the call to create intent - // to avoid crashing. The app will then proceed as the user accepted the permission. - override fun getSynchronousResult(context: Context, input: Unit): SynchronousResult<Boolean>? { - return if (VpnService.prepare(context) == null) { - SynchronousResult(true) - } else { - null - } - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt index e8cd424156..3128870ae5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt @@ -13,7 +13,7 @@ import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy import net.mullvad.mullvadvpn.lib.shared.DeviceRepository import net.mullvad.mullvadvpn.lib.shared.LocaleRepository import net.mullvad.mullvadvpn.lib.shared.RelayLocationTranslationRepository -import net.mullvad.mullvadvpn.lib.shared.VpnPermissionRepository +import net.mullvad.mullvadvpn.lib.shared.VpnProfileUseCase import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named import org.koin.dsl.module @@ -29,11 +29,13 @@ val appModule = module { scope = MainScope(), ) } + + single { VpnProfileUseCase(androidContext()) } + single { BuildVersion(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE) } single { IntentProvider() } single { AccountRepository(get(), get(), MainScope()) } single { DeviceRepository(get()) } - single { VpnPermissionRepository(androidContext()) } single { ConnectionProxy(get(), get(), get()) } single { LocaleRepository(get()) } single { RelayLocationTranslationRepository(get(), get(), MainScope()) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index 1d62de5bb2..f43f1caf8f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -91,7 +91,7 @@ import net.mullvad.mullvadvpn.viewmodel.SplitTunnelingViewModel import net.mullvad.mullvadvpn.viewmodel.Udp2TcpSettingsViewModel import net.mullvad.mullvadvpn.viewmodel.ViewLogsViewModel import net.mullvad.mullvadvpn.viewmodel.VoucherDialogViewModel -import net.mullvad.mullvadvpn.viewmodel.VpnPermissionViewModel +import net.mullvad.mullvadvpn.viewmodel.VpnProfileViewModel import net.mullvad.mullvadvpn.viewmodel.VpnSettingsViewModel import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel import net.mullvad.mullvadvpn.viewmodel.WireguardCustomPortDialogViewModel @@ -206,7 +206,6 @@ val uiModule = module { get(), get(), get(), - get(), IS_PLAY_BUILD, get(named(SELF_PACKAGE_NAME)), ) @@ -237,7 +236,7 @@ val uiModule = module { viewModel { DeleteCustomListConfirmationViewModel(get(), get()) } viewModel { ServerIpOverridesViewModel(get(), get()) } viewModel { ResetServerIpOverridesConfirmationViewModel(get()) } - viewModel { VpnPermissionViewModel(get(), get()) } + viewModel { VpnProfileViewModel(get(), get()) } viewModel { ApiAccessListViewModel(get()) } viewModel { EditApiAccessMethodViewModel(get(), get(), get()) } viewModel { SaveApiAccessMethodViewModel(get(), get()) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/BootCompletedReceiver.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/BootCompletedReceiver.kt index 9f153e724b..3ab3750c5e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/BootCompletedReceiver.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/receiver/BootCompletedReceiver.kt @@ -3,10 +3,10 @@ package net.mullvad.mullvadvpn.receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.net.VpnService import co.touchlab.kermit.Logger import net.mullvad.mullvadvpn.lib.common.constant.KEY_CONNECT_ACTION import net.mullvad.mullvadvpn.lib.common.constant.VPN_SERVICE_CLASS +import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe class BootCompletedReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -16,7 +16,7 @@ class BootCompletedReceiver : BroadcastReceiver() { } private fun startAndConnectTunnel(context: Context) { - val hasVpnPermission = VpnService.prepare(context) == null + val hasVpnPermission = context.prepareVpnSafe().isRight() Logger.i("AutoStart on boot and connect, hasVpnPermission: $hasVpnPermission") if (hasVpnPermission) { val intent = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt index 5572f93961..42838d75d6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt @@ -21,12 +21,12 @@ import net.mullvad.mullvadvpn.compose.state.ConnectUiState import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect import net.mullvad.mullvadvpn.lib.model.ConnectError import net.mullvad.mullvadvpn.lib.model.DeviceState +import net.mullvad.mullvadvpn.lib.model.PrepareError import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy import net.mullvad.mullvadvpn.lib.shared.DeviceRepository -import net.mullvad.mullvadvpn.lib.shared.VpnPermissionRepository import net.mullvad.mullvadvpn.repository.InAppNotificationController import net.mullvad.mullvadvpn.repository.NewDeviceRepository import net.mullvad.mullvadvpn.usecase.LastKnownLocationUseCase @@ -48,7 +48,6 @@ class ConnectViewModel( private val paymentUseCase: PaymentUseCase, private val connectionProxy: ConnectionProxy, lastKnownLocationUseCase: LastKnownLocationUseCase, - private val vpnPermissionRepository: VpnPermissionRepository, private val resources: Resources, private val isPlayBuild: Boolean, private val packageName: String, @@ -138,23 +137,20 @@ class ConnectViewModel( viewModelScope.launch { connectionProxy.connect().onLeft { connectError -> when (connectError) { - ConnectError.NoVpnPermission -> _uiSideEffect.send(UiSideEffect.NoVpnPermission) - is ConnectError.Unknown -> { - _uiSideEffect.send(UiSideEffect.ConnectError.Generic) - } + is ConnectError.Unknown -> _uiSideEffect.send(UiSideEffect.ConnectError.Generic) + is ConnectError.NotPrepared -> + _uiSideEffect.send(UiSideEffect.NotPrepared(connectError.error)) } } } } - fun requestVpnPermissionResult(hasVpnPermission: Boolean) { + fun createVpnProfileResult(hasVpnPermission: Boolean) { viewModelScope.launch { if (hasVpnPermission) { connectionProxy.connect() } else { - vpnPermissionRepository.getAlwaysOnVpnAppName()?.let { - _uiSideEffect.send(UiSideEffect.ConnectError.AlwaysOnVpn(it)) - } ?: _uiSideEffect.send(UiSideEffect.ConnectError.NoVpnPermission) + _uiSideEffect.send(UiSideEffect.ConnectError.PermissionDenied) } } } @@ -206,14 +202,12 @@ class ConnectViewModel( data object RevokedDevice : UiSideEffect - data object NoVpnPermission : UiSideEffect + data class NotPrepared(val prepareError: PrepareError) : UiSideEffect sealed interface ConnectError : UiSideEffect { data object Generic : ConnectError - data object NoVpnPermission : ConnectError - - data class AlwaysOnVpn(val appName: String) : ConnectError + data object PermissionDenied : ConnectError } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnPermissionViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnProfileViewModel.kt index 1e5972b538..cb1a2862bf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnPermissionViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnProfileViewModel.kt @@ -9,19 +9,19 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PERMISSION +import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PROFILE import net.mullvad.mullvadvpn.lib.intent.IntentProvider import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy -class VpnPermissionViewModel( +class VpnProfileViewModel( intentProvider: IntentProvider, private val connectionProxy: ConnectionProxy, ) : ViewModel() { - val uiSideEffect: Flow<VpnPermissionSideEffect> = + val uiSideEffect: Flow<VpnProfileSideEffect> = intentProvider.intents - .filter { it?.action == KEY_REQUEST_VPN_PERMISSION } + .filter { it?.action == KEY_REQUEST_VPN_PROFILE } .distinctUntilChanged() - .map { VpnPermissionSideEffect.ShowDialog } + .map { VpnProfileSideEffect.RequestVpnProfile } .shareIn(viewModelScope, SharingStarted.WhileSubscribed()) fun connect() { @@ -29,6 +29,6 @@ class VpnPermissionViewModel( } } -sealed interface VpnPermissionSideEffect { - data object ShowDialog : VpnPermissionSideEffect +sealed interface VpnProfileSideEffect { + data object RequestVpnProfile : VpnProfileSideEffect } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt index 3dada2a433..0d61b3e300 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt @@ -30,7 +30,6 @@ import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken import net.mullvad.mullvadvpn.lib.shared.AccountRepository import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy import net.mullvad.mullvadvpn.lib.shared.DeviceRepository -import net.mullvad.mullvadvpn.lib.shared.VpnPermissionRepository import net.mullvad.mullvadvpn.repository.InAppNotification import net.mullvad.mullvadvpn.repository.InAppNotificationController import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager @@ -87,9 +86,6 @@ class ConnectViewModelTest { // Last known location private val mockLastKnownLocationUseCase: LastKnownLocationUseCase = mockk() - // VpnPermissionRepository - private val mockVpnPermissionRepository: VpnPermissionRepository = mockk(relaxed = true) - @BeforeEach fun setup() { every { mockServiceConnectionManager.connectionState } returns serviceConnectionState @@ -124,7 +120,6 @@ class ConnectViewModelTest { selectedLocationTitleUseCase = mockSelectedLocationTitleUseCase, connectionProxy = mockConnectionProxy, lastKnownLocationUseCase = mockLastKnownLocationUseCase, - vpnPermissionRepository = mockVpnPermissionRepository, resources = mockk(), isPlayBuild = false, packageName = "net.mullvad.mullvadvpn", diff --git a/android/lib/common/build.gradle.kts b/android/lib/common/build.gradle.kts index 2c4fbe0233..c8554b52c7 100644 --- a/android/lib/common/build.gradle.kts +++ b/android/lib/common/build.gradle.kts @@ -31,10 +31,11 @@ android { dependencies { implementation(projects.lib.model) implementation(projects.lib.resource) - implementation(projects.lib.talpid) + implementation(libs.arrow) implementation(libs.androidx.appcompat) implementation(libs.jodatime) implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) + implementation(libs.kermit) } 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() +} diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt index fe0222596b..0412871f43 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt @@ -35,6 +35,9 @@ import net.mullvad.mullvadvpn.lib.model.DnsState import net.mullvad.mullvadvpn.lib.model.Endpoint import net.mullvad.mullvadvpn.lib.model.ErrorState import net.mullvad.mullvadvpn.lib.model.ErrorStateCause +import net.mullvad.mullvadvpn.lib.model.ErrorStateCause.AuthFailed +import net.mullvad.mullvadvpn.lib.model.ErrorStateCause.OtherAlwaysOnApp +import net.mullvad.mullvadvpn.lib.model.ErrorStateCause.TunnelParameterError import net.mullvad.mullvadvpn.lib.model.FeatureIndicator import net.mullvad.mullvadvpn.lib.model.GeoIpLocation import net.mullvad.mullvadvpn.lib.model.GeoLocationId @@ -76,49 +79,78 @@ import org.joda.time.Instant internal fun ManagementInterface.TunnelState.toDomain(): TunnelState = when (stateCase!!) { - ManagementInterface.TunnelState.StateCase.DISCONNECTED -> - TunnelState.Disconnected( - location = - with(disconnected) { - if (hasDisconnectedLocation()) { - disconnectedLocation.toDomain() - } else null - } - ) - ManagementInterface.TunnelState.StateCase.CONNECTING -> - TunnelState.Connecting( - endpoint = connecting.relayInfo.tunnelEndpoint.toDomain(), - location = - with(connecting.relayInfo) { - if (hasLocation()) { - location.toDomain() - } else null - }, - featureIndicators = connecting.featureIndicators.toDomain(), - ) - ManagementInterface.TunnelState.StateCase.CONNECTED -> - TunnelState.Connected( - endpoint = connected.relayInfo.tunnelEndpoint.toDomain(), - location = - with(connected.relayInfo) { - if (hasLocation()) { - location.toDomain() - } else { - null - } - }, - featureIndicators = connected.featureIndicators.toDomain(), - ) - ManagementInterface.TunnelState.StateCase.DISCONNECTING -> - TunnelState.Disconnecting( - actionAfterDisconnect = disconnecting.afterDisconnect.toDomain() - ) - ManagementInterface.TunnelState.StateCase.ERROR -> - TunnelState.Error(errorState = error.errorState.toDomain()) + ManagementInterface.TunnelState.StateCase.DISCONNECTED -> disconnected.toDomain() + ManagementInterface.TunnelState.StateCase.CONNECTING -> connecting.toDomain() + ManagementInterface.TunnelState.StateCase.CONNECTED -> connected.toDomain() + ManagementInterface.TunnelState.StateCase.DISCONNECTING -> disconnecting.toDomain() + ManagementInterface.TunnelState.StateCase.ERROR -> error.toDomain() ManagementInterface.TunnelState.StateCase.STATE_NOT_SET -> TunnelState.Disconnected(location = disconnected.disconnectedLocation.toDomain()) } +private fun ManagementInterface.TunnelState.Connecting.toDomain(): TunnelState.Connecting = + TunnelState.Connecting( + endpoint = relayInfo.tunnelEndpoint.toDomain(), + location = + if (relayInfo.hasLocation()) { + relayInfo.location.toDomain() + } else null, + featureIndicators = featureIndicators.toDomain(), + ) + +private fun ManagementInterface.TunnelState.Disconnected.toDomain(): TunnelState.Disconnected = + TunnelState.Disconnected( + location = + if (hasDisconnectedLocation()) { + disconnectedLocation.toDomain() + } else null + ) + +private fun ManagementInterface.TunnelState.Connected.toDomain(): TunnelState.Connected = + TunnelState.Connected( + endpoint = relayInfo.tunnelEndpoint.toDomain(), + location = + if (relayInfo.hasLocation()) { + relayInfo.location.toDomain() + } else { + null + }, + featureIndicators = featureIndicators.toDomain(), + ) + +private fun ManagementInterface.TunnelState.Disconnecting.toDomain(): TunnelState.Disconnecting = + TunnelState.Disconnecting(actionAfterDisconnect = afterDisconnect.toDomain()) + +private fun ManagementInterface.TunnelState.Error.toDomain(): TunnelState.Error { + val otherAlwaysOnAppError = + errorState.let { + if (it.hasOtherAlwaysOnAppError()) { + OtherAlwaysOnApp(it.otherAlwaysOnAppError.appName) + } else { + null + } + } + + val invalidDnsServers = + errorState.let { + if (it.hasInvalidDnsServersError()) { + ErrorStateCause.InvalidDnsServers( + it.invalidDnsServersError.ipAddrsList.toList().map { InetAddress.getByName(it) } + ) + } else { + null + } + } + + return TunnelState.Error( + errorState = + errorState.toDomain( + otherAlwaysOnApp = otherAlwaysOnAppError, + invalidDnsServers = invalidDnsServers, + ) + ) +} + internal fun ManagementInterface.GeoIpLocation.toDomain(): GeoIpLocation = GeoIpLocation( ipv4 = @@ -198,12 +230,15 @@ internal fun ManagementInterface.AfterDisconnect.toDomain(): ActionAfterDisconne throw IllegalArgumentException("Unrecognized action after disconnect") } -internal fun ManagementInterface.ErrorState.toDomain(): ErrorState = +internal fun ManagementInterface.ErrorState.toDomain( + otherAlwaysOnApp: ErrorStateCause.OtherAlwaysOnApp?, + invalidDnsServers: ErrorStateCause.InvalidDnsServers?, +): ErrorState = ErrorState( cause = when (cause!!) { ManagementInterface.ErrorState.Cause.AUTH_FAILED -> - ErrorStateCause.AuthFailed(authFailedError.toDomain()) + AuthFailed(authFailedError.toDomain()) ManagementInterface.ErrorState.Cause.IPV6_UNAVAILABLE -> ErrorStateCause.Ipv6Unavailable ManagementInterface.ErrorState.Cause.SET_FIREWALL_POLICY_ERROR -> @@ -212,16 +247,20 @@ internal fun ManagementInterface.ErrorState.toDomain(): ErrorState = ManagementInterface.ErrorState.Cause.START_TUNNEL_ERROR -> ErrorStateCause.StartTunnelError ManagementInterface.ErrorState.Cause.TUNNEL_PARAMETER_ERROR -> - ErrorStateCause.TunnelParameterError(parameterError.toDomain()) + TunnelParameterError(parameterError.toDomain()) ManagementInterface.ErrorState.Cause.IS_OFFLINE -> ErrorStateCause.IsOffline - ManagementInterface.ErrorState.Cause.VPN_PERMISSION_DENIED -> - ErrorStateCause.VpnPermissionDenied ManagementInterface.ErrorState.Cause.SPLIT_TUNNEL_ERROR -> ErrorStateCause.StartTunnelError ManagementInterface.ErrorState.Cause.UNRECOGNIZED, ManagementInterface.ErrorState.Cause.NEED_FULL_DISK_PERMISSIONS, ManagementInterface.ErrorState.Cause.CREATE_TUNNEL_DEVICE -> throw IllegalArgumentException("Unrecognized error state cause") + + ManagementInterface.ErrorState.Cause.NOT_PREPARED -> ErrorStateCause.NotPrepared + ManagementInterface.ErrorState.Cause.OTHER_ALWAYS_ON_APP -> otherAlwaysOnApp!! + ManagementInterface.ErrorState.Cause.OTHER_LEGACY_ALWAYS_ON_VPN -> + ErrorStateCause.OtherLegacyAlwaysOnApp + ManagementInterface.ErrorState.Cause.INVALID_DNS_SERVERS -> invalidDnsServers!! }, isBlocking = !hasBlockingError(), ) diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ConnectError.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ConnectError.kt index 307a235314..6feeeee579 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ConnectError.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ConnectError.kt @@ -3,5 +3,5 @@ package net.mullvad.mullvadvpn.lib.model sealed interface ConnectError { data class Unknown(val throwable: Throwable) : ConnectError - data object NoVpnPermission : ConnectError + data class NotPrepared(val error: PrepareError) : ConnectError } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ErrorStateCause.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ErrorStateCause.kt index ef5947c89a..fdb7dd3a1a 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ErrorStateCause.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/ErrorStateCause.kt @@ -17,7 +17,6 @@ sealed class ErrorStateCause { data object DnsError : ErrorStateCause() - // Regression data class InvalidDnsServers(val addresses: List<InetAddress>) : ErrorStateCause() data object StartTunnelError : ErrorStateCause() @@ -26,7 +25,11 @@ sealed class ErrorStateCause { data object IsOffline : ErrorStateCause() - data object VpnPermissionDenied : ErrorStateCause() + data object NotPrepared : ErrorStateCause() + + data class OtherAlwaysOnApp(val appName: String) : ErrorStateCause() + + data object OtherLegacyAlwaysOnApp : ErrorStateCause() } sealed interface AuthFailedError { diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationAction.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationAction.kt index ec938a9fbf..141fe739f5 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationAction.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationAction.kt @@ -15,6 +15,6 @@ sealed interface NotificationAction { data object Dismiss : Tunnel - data object RequestPermission : Tunnel + data object RequestVpnProfile : Tunnel } } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt index fffe86c247..3ca573a839 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt @@ -1,7 +1,7 @@ package net.mullvad.mullvadvpn.lib.model sealed interface NotificationTunnelState { - data class Disconnected(val hasVpnPermission: Boolean) : NotificationTunnelState + data class Disconnected(val prepareError: PrepareError?) : NotificationTunnelState data object Connecting : NotificationTunnelState @@ -18,7 +18,9 @@ sealed interface NotificationTunnelState { data object VpnPermissionDenied : Error - data object AlwaysOnVpn : Error + data class AlwaysOnVpn(val appName: String) : Error + + data object LegacyLockdown : Error data object Critical : Error } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PrepareError.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PrepareError.kt new file mode 100644 index 0000000000..8954c5f98a --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PrepareError.kt @@ -0,0 +1,17 @@ +package net.mullvad.mullvadvpn.lib.model + +import android.content.Intent + +sealed interface PrepareResult + +sealed interface PrepareError : PrepareResult { + // Result from VpnService.prepare() being invoked with legacy VPN app has always-on + data object OtherLegacyAlwaysOnVpn : PrepareError + + // Prepare gives intent but there is other always VPN app + data class OtherAlwaysOnApp(val appName: String) : PrepareError + + data class NotPrepared(val prepareIntent: Intent) : PrepareError +} + +data object Prepared : PrepareResult diff --git a/android/lib/resource/src/main/res/values-da/strings.xml b/android/lib/resource/src/main/res/values-da/strings.xml index 88c135c304..b3cc919ca4 100644 --- a/android/lib/resource/src/main/res/values-da/strings.xml +++ b/android/lib/resource/src/main/res/values-da/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Alle placeringer</string> <string name="all_providers">Alle udbydere</string> <string name="always_on_vpn_error_notification_content">Kunne ikke starte tunnelforbindelse. Deaktiver Altid-til VPN for <b>%1$s</b>.</string> - <string name="always_on_vpn_error_notification_title">Altid-til VPN tildelt en anden app</string> <string name="any">Enhver</string> <string name="api_access_description">Administrer og tilføj brugerdefinerede metoder for at få adgang til Mullvad API.</string> <string name="api_access_method_info_first_line">Appen skal kommunikere med en Mullvad API-server for at kunne logge dig på, hente serverlister og andre kritiske operationer.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Indtast kuponkode</string> <string name="entry">Indgang</string> <string name="error_occurred">Der opstod en fejl.</string> - <string name="error_state">KUNNE IKKE SIKRE FORBINDELSEN</string> <string name="exclude_applications">Ekskluderede applikationer</string> <string name="exit">Udgang</string> <string name="failed_to_block_internet">Kan ikke blokere al netværkstrafik. Udfør fejlfinding, eller indsend en problemrapport.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Kuponkode er ugyldig.</string> <string name="ipv6_unavailable">Kunne ikke konfigurere IPv6. Deaktiver det i appen, eller aktiver det på din enhed.</string> <string name="is_offline">Din enhed er offline. Tunnelen vil oprette forbindelse automatisk, når din enhed er online igen.</string> + <string name="legacy_always_on_vpn_error_notification_title">Altid-til VPN tildelt en anden app</string> <string name="less_than_a_day_left">mindre end én dag tilbage</string> <string name="less_than_one_day">mindre end én dag</string> <string name="list_name">Listenavn</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Det ser ud til, at du har indtastet et kontonummer i stedet for en rabatkuponkode. Hvis du vil ændre den aktive konto, skal du først logge ud.</string> <string name="voucher_success_title">Indløsning af kuponen lykkedes.</string> <string name="vpn_permission_denied_error">VPN-tilladelse blev nægtet, da tunnelen blev oprettet. Prøv at oprette forbindelse igen.</string> - <string name="vpn_permission_error_notification_message">Altid-til VPN er måske aktiveret for en anden app</string> <string name="vpn_permission_error_notification_title">VPN-tilladelsesfejl</string> <string name="we_will_look_into_this">Vi vil undersøge dette.</string> <string name="wireguard_custon_port_title">Brugerdefineret</string> diff --git a/android/lib/resource/src/main/res/values-de/strings.xml b/android/lib/resource/src/main/res/values-de/strings.xml index fdaceb2899..74331acc9a 100644 --- a/android/lib/resource/src/main/res/values-de/strings.xml +++ b/android/lib/resource/src/main/res/values-de/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Alle Standorte</string> <string name="all_providers">Alle Anbieter</string> <string name="always_on_vpn_error_notification_content">Tunnelverbindung kann nicht gestartet werden. Bitte deaktivieren Sie Always-on VPN für <b>%1$s</b>, bevor Sie Mullvad VPN verwenden.</string> - <string name="always_on_vpn_error_notification_title">Always-on VPN ist einer anderen App zugeordnet</string> <string name="any">Beliebige</string> <string name="api_access_description">Verwaltung und Hinzufügen benutzerdefinierter Methoden für den Zugriff auf die Mullvad-API.</string> <string name="api_access_method_info_first_line">Die App muss mit einem Mullvad API-Server kommunizieren, um Sie anzumelden, Serverlisten abzurufen und andere wichtige Vorgänge durchzuführen.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Gutscheincode eingeben</string> <string name="entry">Eingang</string> <string name="error_occurred">Ein Fehler ist aufgetreten.</string> - <string name="error_state">SICHERE VERBINDUNG KONNTE NICHT HERGESTELLT WERDEN</string> <string name="exclude_applications">Ausgeschlossene Anwendungen</string> <string name="exit">Ausgang</string> <string name="failed_to_block_internet">Der Netzwerk-Traffic konnte nicht gänzlich blockiert werden. Bitte beheben Sie den Fehler oder senden Sie einen Problembericht.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Der Gutscheincode ist ungültig.</string> <string name="ipv6_unavailable">IPv6 konnte nicht konfiguriert werden. Deaktivieren Sie es in der App oder aktivieren Sie es auf Ihrem Gerät.</string> <string name="is_offline">Ihr Gerät ist offline. Der Tunnel wird automatisch verbunden, sobald Ihr Gerät wieder online ist.</string> + <string name="legacy_always_on_vpn_error_notification_title">Always-on VPN ist einer anderen App zugeordnet</string> <string name="less_than_a_day_left">weniger als ein Tag übrig</string> <string name="less_than_one_day">weniger als ein Tag</string> <string name="list_name">Name der Liste</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Anscheinend haben Sie eine Kontonummer statt eines Gutscheincodes eingegeben. Wenn Sie das aktive Konto wechseln möchten, melden Sie sich bitte zuerst ab.</string> <string name="voucher_success_title">Der Gutschein wurde erfolgreich eingelöst.</string> <string name="vpn_permission_denied_error">VPN-Berechtigungen wurden beim Erstellen des Tunnels abgelehnt.</string> - <string name="vpn_permission_error_notification_message">Always-on VPN könnte für eine andere App aktiviert sein</string> <string name="vpn_permission_error_notification_title">VPN-Berechtigungsfehler</string> <string name="we_will_look_into_this">Wir werden uns das anschauen.</string> <string name="wireguard_custon_port_title">Benutzerdefiniert</string> diff --git a/android/lib/resource/src/main/res/values-es/strings.xml b/android/lib/resource/src/main/res/values-es/strings.xml index fb5981905b..512f3a1982 100644 --- a/android/lib/resource/src/main/res/values-es/strings.xml +++ b/android/lib/resource/src/main/res/values-es/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Todas las ubicaciones</string> <string name="all_providers">Todos los proveedores</string> <string name="always_on_vpn_error_notification_content">No se puede iniciar la conexión de túnel. Deshabilite la VPN siempre activa en <b>%1$s</b> antes de utilizar la VPN de Mullvad.</string> - <string name="always_on_vpn_error_notification_title">La VPN siempre activa se ha asignado a otra aplicación</string> <string name="any">Cualquiera</string> <string name="api_access_description">Gestione y añada métodos personalizados para acceder a la API de Mullvad.</string> <string name="api_access_method_info_first_line">La aplicación necesita comunicarse con un servidor API de Mullvad para iniciar su sesión, obtener las listas de servidores y otras operaciones críticas.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Escriba el código del cupón</string> <string name="entry">Entrada</string> <string name="error_occurred">Se produjo un error.</string> - <string name="error_state">NO SE PUDO PROTEGER LA CONEXIÓN</string> <string name="exclude_applications">Aplicaciones excluidas</string> <string name="exit">Salida</string> <string name="failed_to_block_internet">No se puede bloquear todo el tráfico de red. Intente solucionar el problema o envíe un informe de problemas.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">El código del cupón no es válido.</string> <string name="ipv6_unavailable">No se pudo configurar IPv6. Desactívelo en la aplicación o actívelo en el dispositivo.</string> <string name="is_offline">El dispositivo está sin conexión. El túnel se conectará automáticamente cuando el dispositivo vuelva a estar en línea.</string> + <string name="legacy_always_on_vpn_error_notification_title">La VPN siempre activa se ha asignado a otra aplicación</string> <string name="less_than_a_day_left">queda menos de un día</string> <string name="less_than_one_day">menos de un día</string> <string name="list_name">Nombre de la lista</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Parece que ha introducido un número de cuenta en lugar de un código de cupón. Si desea cambiar la cuenta activa, cierre primero la sesión.</string> <string name="voucher_success_title">El cupón se canjeó correctamente.</string> <string name="vpn_permission_denied_error">Se denegó el permiso para usar una conexión VPN al crear el túnel. Intente volver a establecer la conexión.</string> - <string name="vpn_permission_error_notification_message">La VPN siempre activa podría estar habilitada en otra aplicación</string> <string name="vpn_permission_error_notification_title">Error en la autorización de la VPN</string> <string name="we_will_look_into_this">Revisaremos esto.</string> <string name="wireguard_custon_port_title">Personalizado</string> diff --git a/android/lib/resource/src/main/res/values-fi/strings.xml b/android/lib/resource/src/main/res/values-fi/strings.xml index 38e2a76a8d..6a10183e80 100644 --- a/android/lib/resource/src/main/res/values-fi/strings.xml +++ b/android/lib/resource/src/main/res/values-fi/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Kaikki sijainnit</string> <string name="all_providers">Kaikki palveluntarjoajat</string> <string name="always_on_vpn_error_notification_content">Tunneliyhteyden käynnistäminen ei onnistu. Poista aina päällä oleva VPN käytöstä sovellukselle <b>%1$s</b> ennen Mullvad VPN:n käyttämistä.</string> - <string name="always_on_vpn_error_notification_title">Aina päällä oleva VPN on määritetty toiselle sovellukselle</string> <string name="any">Mikä tahansa</string> <string name="api_access_description">Hallitse ja lisää mukautettuja menetelmiä Mullvadin ohjelmointirajapinnan käyttämiseksi.</string> <string name="api_access_method_info_first_line">Sovelluksen on kommunikoitava Mullvadin ohjelmointirajapinnan palvelimen kanssa, jotta sinut voidaan kirjata sisään, palvelinluetteloiden hakemiseksi sekä muiden tärkeiden toimintojen suorittamiseksi.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Syötä kuponkikoodi</string> <string name="entry">Tulo</string> <string name="error_occurred">Ilmeni virhe.</string> - <string name="error_state">YHTEYDEN SUOJAAMINEN EPÄONNISTUI</string> <string name="exclude_applications">Poissuljetut sovellukset</string> <string name="exit">Lähtö</string> <string name="failed_to_block_internet">Kaiken verkkoliikenteen estäminen ei onnistu. Käytä vianetsintää tai lähetä ongelmaraportti.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Kuponkikoodi ei kelpaa.</string> <string name="ipv6_unavailable">IPv6:n määritys ei onnistunut. Poista se käytöstä sovelluksessa tai ota käyttöön laitteellasi.</string> <string name="is_offline">Laitteesi on offline-tilassa. Kun laitteesi on taas online-tilassa, tunneli muodostaa yhteyden automaattisesti.</string> + <string name="legacy_always_on_vpn_error_notification_title">Aina päällä oleva VPN on määritetty toiselle sovellukselle</string> <string name="less_than_a_day_left">alle vuorokausi jäljellä</string> <string name="less_than_one_day">alle vuorokausi</string> <string name="list_name">Luettelon nimi</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Näytät syöttäneen tilin numeron etusetelin koodin sijaan. Jos haluat vaihtaa tiliä, kirjaudu ensin ulos nykyiseltä tililtä.</string> <string name="voucher_success_title">Kupongin lunastus onnistui.</string> <string name="vpn_permission_denied_error">VPN-lupa evättiin tunnelia luotaessa. Yritä muodostaa yhteys uudelleen.</string> - <string name="vpn_permission_error_notification_message">Aina päällä oleva VPN on mahdollisesti otettu käyttöön toiselle sovellukselle</string> <string name="vpn_permission_error_notification_title">VPN-lupavirhe</string> <string name="we_will_look_into_this">Tutkimme asiaa.</string> <string name="wireguard_custon_port_title">Mukautettu</string> diff --git a/android/lib/resource/src/main/res/values-fr/strings.xml b/android/lib/resource/src/main/res/values-fr/strings.xml index cd70ac2701..8ea968806f 100644 --- a/android/lib/resource/src/main/res/values-fr/strings.xml +++ b/android/lib/resource/src/main/res/values-fr/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Toutes les localisations</string> <string name="all_providers">Tous les fournisseurs</string> <string name="always_on_vpn_error_notification_content">Impossible de démarrer la connexion au tunnel. Veuillez désactiver « Toujours exiger un VPN « pour <b>%1$s</b> avant d\'utiliser Mullvad VPN.</string> - <string name="always_on_vpn_error_notification_title">« Toujours exiger un VPN » est assigné à une autre application</string> <string name="any">N\'importe lequel</string> <string name="api_access_description">Gérez et ajoutez des modes d\'accès personnalisés à l\'API Mullvad.</string> <string name="api_access_method_info_first_line">L\'application doit communiquer avec un serveur d\'API Mullvad pour vous connecter, récupérer des listes de serveurs et effectuer d\'autres opérations critiques.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Saisir un code de bon</string> <string name="entry">Entrée</string> <string name="error_occurred">Une erreur est survenue.</string> - <string name="error_state">ÉCHEC DE LA SÉCURISATION DE LA CONNEXION</string> <string name="exclude_applications">Applications exclues</string> <string name="exit">Sortie</string> <string name="failed_to_block_internet">Impossible de bloquer tout le trafic réseau. Veuillez dépanner ou envoyer un rapport de problème.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Le code du bon n\'est pas valide.</string> <string name="ipv6_unavailable">Impossible de configurer IPv6. Désactivez le protocole dans l\'application ou activez-le dans votre appareil.</string> <string name="is_offline">Votre appareil est hors ligne. Le tunnel se connectera automatiquement une fois votre appareil à nouveau en ligne.</string> + <string name="legacy_always_on_vpn_error_notification_title">« Toujours exiger un VPN » est assigné à une autre application</string> <string name="less_than_a_day_left">moins d\'un jour restant</string> <string name="less_than_one_day">moins d\'un jour</string> <string name="list_name">Nom de la liste</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Vous semblez avoir saisi un numéro de compte plutôt qu\'un code de bon. Si vous souhaitez modifier le compte actif, veuillez d\'abord vous déconnecter.</string> <string name="voucher_success_title">Le bon a bien été échangé.</string> <string name="vpn_permission_denied_error">La permission VPN a été refusée lors de la création du tunnel. Veuillez essayer de vous reconnecter.</string> - <string name="vpn_permission_error_notification_message">« Toujours exiger un VPN » est peut-être activé pour une autre application</string> <string name="vpn_permission_error_notification_title">Erreur de permission VPN</string> <string name="we_will_look_into_this">Nous allons nous pencher dessus.</string> <string name="wireguard_custon_port_title">Personnalisé</string> diff --git a/android/lib/resource/src/main/res/values-it/strings.xml b/android/lib/resource/src/main/res/values-it/strings.xml index 70a08dbf7c..629a1b12df 100644 --- a/android/lib/resource/src/main/res/values-it/strings.xml +++ b/android/lib/resource/src/main/res/values-it/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Tutti i luoghi</string> <string name="all_providers">Tutti i fornitori</string> <string name="always_on_vpn_error_notification_content">Impossibile avviare la connessione tunnel. Disabilita VPN sempre attiva per <b>%1$s</b> prima di utilizzare Mullvad VPN.</string> - <string name="always_on_vpn_error_notification_title">VPN sempre attiva assegnata a un\'altra app</string> <string name="any">Qualsiasi</string> <string name="api_access_description">Gestisci e aggiungi metodi personalizzati per accedere all\'API Mullvad.</string> <string name="api_access_method_info_first_line">L\'app deve comunicare con un server API Mullvad per accedere, recuperare elenchi di server e altre operazioni critiche.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Inserisci codice voucher</string> <string name="entry">Ingresso</string> <string name="error_occurred">Si è verificato un errore.</string> - <string name="error_state">IMPOSSIBILE STABILIRE UNA CONNESSIONE PROTETTA</string> <string name="exclude_applications">Applicazioni escluse</string> <string name="exit">Uscita</string> <string name="failed_to_block_internet">Impossibile bloccare tutto il traffico di rete. Consulta la risoluzione dei problemi o invia una segnalazione del problema.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Il codice voucher non è valido.</string> <string name="ipv6_unavailable">Impossibile configurare IPv6. Disabilitalo nell\'app o abilitalo sul tuo dispositivo.</string> <string name="is_offline">Il tuo dispositivo è offline. Il tunnel si connetterà automaticamente una volta che il tuo dispositivo sarà di nuovo online.</string> + <string name="legacy_always_on_vpn_error_notification_title">VPN sempre attiva assegnata a un\'altra app</string> <string name="less_than_a_day_left">meno di un giorno rimanente</string> <string name="less_than_one_day">meno di un giorno</string> <string name="list_name">Nome dell\'elenco</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Sembra che tu abbia inserito un numero di account anziché un codice voucher. Se desideri modificare l\'account attivo, effettua prima la disconnessione.</string> <string name="voucher_success_title">Il voucher è stato riscattato correttamente.</string> <string name="vpn_permission_denied_error">L\'autorizzazione VPN è stata negata durante la creazione del tunnel. Prova a connetterti di nuovo.</string> - <string name="vpn_permission_error_notification_message">La VPN sempre attiva potrebbe essere abilitata per un\'altra app</string> <string name="vpn_permission_error_notification_title">Errore di autorizzazione VPN</string> <string name="we_will_look_into_this">Verificheremo.</string> <string name="wireguard_custon_port_title">Personalizzato</string> diff --git a/android/lib/resource/src/main/res/values-ja/strings.xml b/android/lib/resource/src/main/res/values-ja/strings.xml index 2fb24ebe8e..f7efc1af4a 100644 --- a/android/lib/resource/src/main/res/values-ja/strings.xml +++ b/android/lib/resource/src/main/res/values-ja/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">すべての場所</string> <string name="all_providers">すべてのプロバイダ</string> <string name="always_on_vpn_error_notification_content">トンネル接続を開始できません。Mullvad VPNを使用する前に<b>%1$s</b>のAlways-on VPNを無効にしてください。</string> - <string name="always_on_vpn_error_notification_title">Always-on VPNは他のアプリに割り当てられています</string> <string name="any">すべて</string> <string name="api_access_description">Mullvad APIへのカスタムのアクセス方法を管理・追加します。</string> <string name="api_access_method_info_first_line">アプリはユーザーのログイン、サーバーリストの取得、およびその他の重要な操作を行うためにMullvad APIサーバーと通信する必要があります。</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">バウチャーコードを入力</string> <string name="entry">入口</string> <string name="error_occurred">エラー発生。</string> - <string name="error_state">セキュリティ保護接続を確立できませんでした</string> <string name="exclude_applications">除外対象アプリケーション</string> <string name="exit">出口</string> <string name="failed_to_block_internet">すべてのネットワークトラフィックをブロックできません。問題に対処するか、問題の報告を送信してください。</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">バウチャーコードが無効です。</string> <string name="ipv6_unavailable">IPv6を設定できませんでした。アプリで無効にするか、デバイスで有効にしてください。</string> <string name="is_offline">デバイスがオフラインになっています。トンネルはデバイスがオンラインに戻ると自動的に接続されます。</string> + <string name="legacy_always_on_vpn_error_notification_title">Always-on VPNは他のアプリに割り当てられています</string> <string name="less_than_a_day_left">残り1日未満</string> <string name="less_than_one_day">1日未満</string> <string name="list_name">リスト名</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">バウチャーコードではなくアカウント番号を入力したようです。有効なアカウントを変更する場合は、先にログアウトしてください。</string> <string name="voucher_success_title">バウチャーを正常に使用しました。</string> <string name="vpn_permission_denied_error">トンネルを作成中にVPNへのアクセスが拒否されました。もう一度接続してみてください。</string> - <string name="vpn_permission_error_notification_message">Always-on VPNが別のアプリで有効になっている可能性があります</string> <string name="vpn_permission_error_notification_title">VPN許可エラー</string> <string name="we_will_look_into_this">この問題を調査いたします。</string> <string name="wireguard_custon_port_title">カスタム</string> diff --git a/android/lib/resource/src/main/res/values-ko/strings.xml b/android/lib/resource/src/main/res/values-ko/strings.xml index f8727aabbc..c6d2c3cf3f 100644 --- a/android/lib/resource/src/main/res/values-ko/strings.xml +++ b/android/lib/resource/src/main/res/values-ko/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">모든 위치</string> <string name="all_providers">모든 제공업체</string> <string name="always_on_vpn_error_notification_content">터널 연결을 시작할 수 없습니다. Mullvad VPN을 사용하기 전에 <b>%1$s</b>에 대한 상시 접속 VPN을 비활성화하세요.</string> - <string name="always_on_vpn_error_notification_title">상시 접속 VPN이 다른 앱에 할당됨</string> <string name="any">모두</string> <string name="api_access_description">Mullvad API에 액세스하기 위한 사용자 지정 방법을 관리하고 추가합니다.</string> <string name="api_access_method_info_first_line">이 앱은 로그인, 서버 목록 가져오기 및 기타 중요한 작업을 위해 Mullvad API 서버와 통신해야 합니다.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">바우처 코드 입력</string> <string name="entry">시작</string> <string name="error_occurred">오류가 발생했습니다.</string> - <string name="error_state">보안 연결 실패</string> <string name="exclude_applications">제외된 애플리케이션</string> <string name="exit">종료</string> <string name="failed_to_block_internet">모든 네트워크 트래픽을 차단할 수는 없습니다. 문제를 해결하거나 문제 보고서를 보내주세요.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">유효하지 않은 바우처 코드입니다.</string> <string name="ipv6_unavailable">IPv6을 구성할 수 없습니다. 앱에서 비활성화하거나 장치에서 활성화하세요.</string> <string name="is_offline">장치가 오프라인 상태입니다. 장치가 다시 온라인 상태가 되면 터널이 자동으로 연결됩니다.</string> + <string name="legacy_always_on_vpn_error_notification_title">상시 접속 VPN이 다른 앱에 할당됨</string> <string name="less_than_a_day_left">1일 이내</string> <string name="less_than_one_day">1일 미만</string> <string name="list_name">목록 이름</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">바우처 코드 대신 계정 번호를 입력한 것 같습니다. 활성 계정을 변경하려면 먼저 로그아웃하세요.</string> <string name="voucher_success_title">바우처가 성공적으로 사용되었습니다.</string> <string name="vpn_permission_denied_error">터널을 만드는 동안 VPN 사용 권한이 거부되었습니다. 다시 연결해 보세요.</string> - <string name="vpn_permission_error_notification_message">상시 접속 VPN이 다른 앱에 활성화되었을 수 있습니다.</string> <string name="vpn_permission_error_notification_title">VPN 권한 오류</string> <string name="we_will_look_into_this">조사해보겠습니다.</string> <string name="wireguard_custon_port_title">사용자 지정</string> diff --git a/android/lib/resource/src/main/res/values-my/strings.xml b/android/lib/resource/src/main/res/values-my/strings.xml index 5c9cd497f2..a73a327cb9 100644 --- a/android/lib/resource/src/main/res/values-my/strings.xml +++ b/android/lib/resource/src/main/res/values-my/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">တည်နေရာအာလုံး</string> <string name="all_providers">ပံ့ပိုးသူအားလုံး</string> <string name="always_on_vpn_error_notification_content">Tunnel ချိတ်ဆက်မှုကို စတင်၍ မရနိုင်ပါ။ Mullvad VPN ကို မသုံးမီ <b>%1$s</b> အတွက် VPN အမြဲဖွင့်ထားမှုကို ပိတ်ပေးပါ။</string> - <string name="always_on_vpn_error_notification_title">အမြဲဖွင့် VPN ကို အခြားအက်ပ်တစ်ခုသို့ သတ်မှတ်ထားပါသည်</string> <string name="any">တစ်ခုခု</string> <string name="api_access_description">Mullvad API ကို ရယူသုံးစွဲရန် စိတ်ကြိုက် နည်းလမ်းများကို ပေါင်းထည့်၍ စီမံပါ။</string> <string name="api_access_method_info_first_line">Mullvad API ဆာဗာသို့ သင်ဝင်ရောက်ရန်၊ ဆာဗာစာရင်းများ ရယူရန်နှင့် အလွန်အရေးပါသည့် အခြားလုပ်ဆောင်မှုများအတွက် ဤအက်ပ်သည် ၎င်းနှင့်ဆက်သွယ်ရန် လိုအပ်ပါသည်။</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">ဘောက်ချာကုဒ် ဖြည့်သွင်းရန်</string> <string name="entry">အဝင်</string> <string name="error_occurred">ချို့ယွင်းချက် ဖြစ်ပေါ်ခဲ့ပါသည်။</string> - <string name="error_state">ချိတ်ဆက်မှုကို ကာကွယ်ရန် မအောင်မြင်ပါ</string> <string name="exclude_applications">အပလီကေးရှင်းများ ဖယ်ထားပြီး</string> <string name="exit">အထွက်</string> <string name="failed_to_block_internet">ကွန်ရက် ကူးလူးမှု အားလုံးကို ပိတ်ဆို့၍ မရနိုင်ပါ။ ပြစ်ချက် ရှာဖွေဖယ်ရှားပေးပါ သို့မဟုတ် ပြဿနာ ရီပို့တ်တစ်ခု ပေးပို့ပါ။</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">ဘောက်ချာကုဒ် မှားနေပါသည်။</string> <string name="ipv6_unavailable">IPv6 ကို သတ်မှတ်ချိန်ညှိ၍ မရနိုင်ပါ။ အက်ပ်တွင် ၎င်းကို ပိတ်ပါ သို့မဟုတ် သင့်စက်တွင် ၎င်းကို ဖွင့်ပါ။</string> <string name="is_offline">သင့်စက်သည် အော့ဖ်လိုင်း ဖြစ်နေပါသည်။ သင့်စက် အွန်လိုင်း ပြန်ဖြစ်သည်နှင့် Tunnel သည် အော်တို ချိတ်ဆက်သွားပါမည်။</string> + <string name="legacy_always_on_vpn_error_notification_title">အမြဲဖွင့် VPN ကို အခြားအက်ပ်တစ်ခုသို့ သတ်မှတ်ထားပါသည်</string> <string name="less_than_a_day_left">တစ်ရက်အောက်သာ ကျန်တော့သည်</string> <string name="less_than_one_day">တစ်ရက်အောက် နည်းသည်။</string> <string name="list_name">စာရင်း အမည်</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">ဘောက်ချာကုဒ်အစား အကောင့်နံပါတ်တစ်ခုကို ထည့်သွင်းထားပုံရသည်။ အသုံးပြုနေသောအကောင့်ကို ပြောင်းလဲလိုပါက ဦးစွာ အကောင့်မှထွက်ပါ။</string> <string name="voucher_success_title">ဘောက်ချာကို အောင်မြင်စွာ လဲယူခဲ့ပါသည်။</string> <string name="vpn_permission_denied_error">Tunnel ဖန်တီးနေစဉ် VPN ခွင့်ပြုချက်ကို ပယ်ချခဲ့ပါသည်။ ထပ်မံချိတ်ဆက်ပေးပါ။</string> - <string name="vpn_permission_error_notification_message">အမြဲဖွင့် VPN ကို နောက်ထပ်အက်ပ်အတွက် ဖွင့်ထားနိုင်ပါသည်</string> <string name="vpn_permission_error_notification_title">VPN ခွင့်ပြုချက် ချို့ယွင်းချက်</string> <string name="we_will_look_into_this">ဤသည်ကို စစ်ဆေးလိုက်ပါမည်။</string> <string name="wireguard_custon_port_title">စိတ်ကြိုက်</string> diff --git a/android/lib/resource/src/main/res/values-nb/strings.xml b/android/lib/resource/src/main/res/values-nb/strings.xml index bef18f4629..01a2e1c0cf 100644 --- a/android/lib/resource/src/main/res/values-nb/strings.xml +++ b/android/lib/resource/src/main/res/values-nb/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Alle steder</string> <string name="all_providers">Alle leverandører</string> <string name="always_on_vpn_error_notification_content">Kunne ikke starte tunneltilkobling. Deaktiver VPN som alltid er på, for <b>%1$s</b> før du bruker Mullvad VPN.</string> - <string name="always_on_vpn_error_notification_title">VPN som alltid er på, er tilordnet en annen app</string> <string name="any">Hvilken som helst</string> <string name="api_access_description">Administrer og legg til tilpassede metoder for tilgang til Mullvad API.</string> <string name="api_access_method_info_first_line">Appen må kunne kommunisere med en Mullvad API-server for å logge deg inn, hente serverlister og utføre andre kritiske operasjoner.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Skriv inn kupongkode</string> <string name="entry">Inngang</string> <string name="error_occurred">Det oppstod en feil.</string> - <string name="error_state">KUNNE IKKE OPPRETTE SIKKER TILKOBLING</string> <string name="exclude_applications">Ekskluder applikasjoner</string> <string name="exit">Utgang</string> <string name="failed_to_block_internet">Kunne ikke blokkere all nettverkstrafikk. Feilsøk eller send inn en problemrapport.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Ugyldig kupongkode.</string> <string name="ipv6_unavailable">Kunne ikke konfigurere IPv6. Deaktiver den i appen eller aktiver den på enheten din.</string> <string name="is_offline">Enheten er frakoblet. Tunnelen vil koble til automatisk når enheten er tilkoblet internett igjen.</string> + <string name="legacy_always_on_vpn_error_notification_title">VPN som alltid er på, er tilordnet en annen app</string> <string name="less_than_a_day_left">mindre enn én dag igjen</string> <string name="less_than_one_day">mindre enn én dag</string> <string name="list_name">Listenavn</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Det ser ut til at du har oppgitt et kontonummer i stedet for en kupongkode. Hvis du vil endre den aktive kontoen, må du først logge ut.</string> <string name="voucher_success_title">Kupongkoden er løst inn.</string> <string name="vpn_permission_denied_error">VPN-tillatelse ble avvist under opprettelsen av tunnelen. Prøv å koble til igjen.</string> - <string name="vpn_permission_error_notification_message">VPN som alltid er på, kan være aktivert for en annen app</string> <string name="vpn_permission_error_notification_title">Feil med VPN-tillatelse</string> <string name="we_will_look_into_this">Dette skal vi følge opp.</string> <string name="wireguard_custon_port_title">Egendefinert</string> diff --git a/android/lib/resource/src/main/res/values-nl/strings.xml b/android/lib/resource/src/main/res/values-nl/strings.xml index e965388f40..84ee272e08 100644 --- a/android/lib/resource/src/main/res/values-nl/strings.xml +++ b/android/lib/resource/src/main/res/values-nl/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Alle locaties</string> <string name="all_providers">Alle aanbieders</string> <string name="always_on_vpn_error_notification_content">Kan de tunnelverbinding niet starten. Schakel Altijd-aan VPN uit voor <b>%1$s</b> voordat u Mullvad VPN gebruikt.</string> - <string name="always_on_vpn_error_notification_title">Altijd-aan VPN toegewezen aan andere app</string> <string name="any">Elke</string> <string name="api_access_description">Beheer en voeg aangepaste methoden toe om toegang te krijgen tot de Mullvad-API.</string> <string name="api_access_method_info_first_line">De app moet communiceren met een Mullvad-API-server om u aan te melden, serverlijsten op te halen en andere belangrijke handelingen uit te voeren.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Vouchercode invoeren</string> <string name="entry">Ingang</string> <string name="error_occurred">Er is een fout opgetreden.</string> - <string name="error_state">VERBINDING BEVEILIGEN MISLUKT</string> <string name="exclude_applications">Uitgesloten toepassingen</string> <string name="exit">Uitgang</string> <string name="failed_to_block_internet">Kan niet alle netwerkverkeer blokkeren. Los problemen op of stuur een probleemmelding.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Vouchercode is ongeldig.</string> <string name="ipv6_unavailable">Kon IPv6 niet configureren. Schakel het uit in de app of schakel het in op uw apparaat.</string> <string name="is_offline">Uw apparaat is offline. De tunnel wordt automatisch verbonden wanneer uw apparaat weer online is.</string> + <string name="legacy_always_on_vpn_error_notification_title">Altijd-aan VPN toegewezen aan andere app</string> <string name="less_than_a_day_left">minder dan een dag over</string> <string name="less_than_one_day">minder dan één dag</string> <string name="list_name">Lijstnaam</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Het lijkt erop dat u een accountnummer hebt ingevoerd in plaats van een vouchercode. Als u het actieve account wilt wijzigen, meld u dan eerst af.</string> <string name="voucher_success_title">Voucher is ingewisseld.</string> <string name="vpn_permission_denied_error">VPN-toestemming is geweigerd tijdens maken van de tunnel. Probeer opnieuw verbinding te maken.</string> - <string name="vpn_permission_error_notification_message">Altijd-aan VPN is mogelijk ingeschakeld voor een andere app</string> <string name="vpn_permission_error_notification_title">VPN-machtigingsfout</string> <string name="we_will_look_into_this">We gaan het bekijken.</string> <string name="wireguard_custon_port_title">Aangepast</string> diff --git a/android/lib/resource/src/main/res/values-pl/strings.xml b/android/lib/resource/src/main/res/values-pl/strings.xml index 9fd1e9de55..e19d2fddd4 100644 --- a/android/lib/resource/src/main/res/values-pl/strings.xml +++ b/android/lib/resource/src/main/res/values-pl/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Wszystkie lokalizacje</string> <string name="all_providers">Wszyscy dostawcy</string> <string name="always_on_vpn_error_notification_content">Nie można uruchomić połączenia tunelowego. Przed rozpoczęciem użytkowania usługi Mullvad VPN wyłącz opcję „Zawsze włączony VPN” w <b>%1$s</b>.</string> - <string name="always_on_vpn_error_notification_title">Opcja „Zawsze włączony VPN” przypisana jest do innej aplikacji</string> <string name="any">Dowolny</string> <string name="api_access_description">Zarządzaj i dodawaj niestandardowe metody dostępu do interfejsu API Mullvad.</string> <string name="api_access_method_info_first_line">Aplikacja musi komunikować się z serwerem API Mullvad, aby można było się zalogować, pobrać listy serwerów i wykonywać inne krytyczne operacje.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Wprowadź kod kuponu</string> <string name="entry">Wejście</string> <string name="error_occurred">Wystąpił błąd.</string> - <string name="error_state">BŁĄD ZABEZPIECZANIA POŁĄCZENIA</string> <string name="exclude_applications">Wykluczone aplikacje</string> <string name="exit">Wyjście</string> <string name="failed_to_block_internet">Nie można zablokować całego ruchu sieciowego. Rozwiąż problem lub wyślij zgłoszenie problemu.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Nieprawidłowy kod kuponu.</string> <string name="ipv6_unavailable">Nie można skonfigurować protokołu IPv6. Wyłącz go w aplikacji lub włącz na urządzeniu.</string> <string name="is_offline">Twoje urządzenie jest offline. Tunel zostanie automatycznie połączony, gdy urządzenie ponownie przejdzie w tryb online.</string> + <string name="legacy_always_on_vpn_error_notification_title">Opcja „Zawsze włączony VPN” przypisana jest do innej aplikacji</string> <string name="less_than_a_day_left">pozostał mniej niż jeden dzień</string> <string name="less_than_one_day">mniej niż jeden dzień</string> <string name="list_name">Nazwa listy</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Wygląda na to, że wpisano numer konta zamiast kodu kuponu. Jeśli chcesz zmienić aktywne konto, najpierw się wyloguj.</string> <string name="voucher_success_title">Kupon został zrealizowany.</string> <string name="vpn_permission_denied_error">Uprawnienie VPN zostało odrzucone podczas tworzenia tunelu. Spróbuj połączyć się ponownie.</string> - <string name="vpn_permission_error_notification_message">Opcja „Zawsze włączony VPN” może być włączona dla innej aplikacji</string> <string name="vpn_permission_error_notification_title">Błąd uprawnienia VPN</string> <string name="we_will_look_into_this">Sprawdzimy to.</string> <string name="wireguard_custon_port_title">Niestandardowy</string> diff --git a/android/lib/resource/src/main/res/values-pt/strings.xml b/android/lib/resource/src/main/res/values-pt/strings.xml index 34175af582..0e390b6a30 100644 --- a/android/lib/resource/src/main/res/values-pt/strings.xml +++ b/android/lib/resource/src/main/res/values-pt/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Todas as localizações</string> <string name="all_providers">Todos os fornecedores</string> <string name="always_on_vpn_error_notification_content">Não foi possível iniciar a ligação de túnel. Desative a VPN sempre ligada para <b>%1$s</b> antes de utilizar a Mullvad VPN.</string> - <string name="always_on_vpn_error_notification_title">VPN sempre ligada atribuída a outra app</string> <string name="any">Qualquer</string> <string name="api_access_description">Gerir e adicionar métodos personalizados para aceder à Mullvad API.</string> <string name="api_access_method_info_first_line">A app precisa de comunicar com um servidor da Mullvad API para iniciar a sua sessão, obter listas de servidores e outras operações críticas.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Introduza o código do voucher</string> <string name="entry">Entrada</string> <string name="error_occurred">Ocorreu um erro.</string> - <string name="error_state">ERRO AO ESTABELECER LIGAÇÃO SEGURA</string> <string name="exclude_applications">Aplicações excluídas</string> <string name="exit">Saída</string> <string name="failed_to_block_internet">Não foi possível bloquear todo o tráfego de rede. Experimente a resolução de problemas ou envie um relatório do problema.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Código do voucher inválido.</string> <string name="ipv6_unavailable">Não foi possível configurar o IPv6. Desative-o ou ative-o no seu dispositivo.</string> <string name="is_offline">O seu dispositivo está offline. O túnel irá ligar-se automaticamente assim que o seu dispositivo estiver novamente online.</string> + <string name="legacy_always_on_vpn_error_notification_title">VPN sempre ligada atribuída a outra app</string> <string name="less_than_a_day_left">menos de um dia restante</string> <string name="less_than_one_day">menos de um dia</string> <string name="list_name">Nome da lista</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Parece que introduziu um número de conta em vez de um código de voucher. Se pretender alterar a conta ativa, termine a sessão primeiro.</string> <string name="voucher_success_title">O voucher foi reclamado com sucesso.</string> <string name="vpn_permission_denied_error">A transmissão foi negada durante a criação do túnel. Tente fazer novamente a ligação.</string> - <string name="vpn_permission_error_notification_message">A VPN sempre ligada pode estar ativada para outra app</string> <string name="vpn_permission_error_notification_title">Erro de permissão da VPN</string> <string name="we_will_look_into_this">Vamos analisar esta situação.</string> <string name="wireguard_custon_port_title">Personalizado</string> diff --git a/android/lib/resource/src/main/res/values-ru/strings.xml b/android/lib/resource/src/main/res/values-ru/strings.xml index 4d7e03a968..060597a298 100644 --- a/android/lib/resource/src/main/res/values-ru/strings.xml +++ b/android/lib/resource/src/main/res/values-ru/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Все местоположения</string> <string name="all_providers">Все провайдеры</string> <string name="always_on_vpn_error_notification_content">Не удалось запустить туннельное подключение. Перед использованием Mullvad VPN отключите опцию «Постоянная VPN» для приложения <b>%1$s</b>.</string> - <string name="always_on_vpn_error_notification_title">Опция «Постоянная VPN» назначена другому приложению</string> <string name="any">Все</string> <string name="api_access_description">Добавление пользовательских методов для доступа к API Mullvad и управление ими.</string> <string name="api_access_method_info_first_line">Приложение должно взаимодействовать с сервером API Mullvad для входа в учетную запись, получения списков серверов и других важных операций.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Введите код ваучера</string> <string name="entry">Вход</string> <string name="error_occurred">Произошла ошибка.</string> - <string name="error_state">НЕ УДАЛОСЬ УСТАНОВИТЬ БЕЗОПАСНОЕ ПОДКЛЮЧЕНИЕ</string> <string name="exclude_applications">Исключенные приложения</string> <string name="exit">Выход</string> <string name="failed_to_block_internet">Не удалось заблокировать весь сетевой трафик. Устраните неполадки или отправьте сообщение о проблеме.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Код ваучера недействителен.</string> <string name="ipv6_unavailable">Не удается сконфигурировать IPv6. Отключите этот протокол в приложении или включите на устройстве.</string> <string name="is_offline">Устройство отключено от Интернета. Туннельное подключение автоматически запустится, как только ваше устройство подключится к Интернету.</string> + <string name="legacy_always_on_vpn_error_notification_title">Опция «Постоянная VPN» назначена другому приложению</string> <string name="less_than_a_day_left">осталось менее суток</string> <string name="less_than_one_day">менее суток</string> <string name="list_name">Имя списка</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Вы ввели номер учетной записи вместо кода ваучера. Чтобы изменить активную учетную запись, сначала выйдите из системы.</string> <string name="voucher_success_title">Ваучер погашен.</string> <string name="vpn_permission_denied_error">При создании туннеля в доступе к VPN было отказано. Попробуйте подключиться снова.</string> - <string name="vpn_permission_error_notification_message">Опцию «Постоянная VPN» может быть включена для другого приложения</string> <string name="vpn_permission_error_notification_title">Ошибка разрешения для VPN</string> <string name="we_will_look_into_this">Мы рассмотрим эту проблему.</string> <string name="wireguard_custon_port_title">Пользовательский</string> diff --git a/android/lib/resource/src/main/res/values-sv/strings.xml b/android/lib/resource/src/main/res/values-sv/strings.xml index a1f0557de7..35517f1c95 100644 --- a/android/lib/resource/src/main/res/values-sv/strings.xml +++ b/android/lib/resource/src/main/res/values-sv/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Alla platser</string> <string name="all_providers">Alla leverantörer</string> <string name="always_on_vpn_error_notification_content">Det går inte att starta tunnelanslutning. Aktivera VPN som alltid är på för <b>%1$s</b> innan du använder Mullvad VPN.</string> - <string name="always_on_vpn_error_notification_title">VPN som alltid är på har tilldelats till annan app</string> <string name="any">Valfri</string> <string name="api_access_description">Hantera och lägg till anpassade metoder för att komma åt Mullvad API.</string> <string name="api_access_method_info_first_line">Appen måste kommunicera med en Mullvad API-server för att logga in dig, hämta serverlistor och andra viktiga åtgärder.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Ange kupongkod</string> <string name="entry">Ingång</string> <string name="error_occurred">Ett fel har inträffat.</string> - <string name="error_state">DET GICK INTE ATT SÄKRA ANSLUTNINGEN</string> <string name="exclude_applications">Exkluderade applikationer</string> <string name="exit">Utgång</string> <string name="failed_to_block_internet">Det går inte att blockera all nätverkstrafik. Felsök eller skicka en problemrapport.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Kupongkoden är ogiltig.</string> <string name="ipv6_unavailable">Det gick inte att konfigurera IPv6. Inaktivera det i appen eller aktivera det på din enhet.</string> <string name="is_offline">Din enhet är offline. Tunneln ansluts automatiskt när din enhet är online igen.</string> + <string name="legacy_always_on_vpn_error_notification_title">VPN som alltid är på har tilldelats till annan app</string> <string name="less_than_a_day_left">mindre än en dag kvar</string> <string name="less_than_one_day">mindre än en dag</string> <string name="list_name">Listnamn</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Det verkar som om du angett ett kontonummer istället för en kupongkod. Logga först ut om du vill ändra den aktiva koden.</string> <string name="voucher_success_title">Kupongen har lösts in.</string> <string name="vpn_permission_denied_error">VPN-behörighet nekades när tunneln skapades. Försök att ansluta igen.</string> - <string name="vpn_permission_error_notification_message">VPN som alltid är på kan ha aktiverats för annan app</string> <string name="vpn_permission_error_notification_title">Behörighetsfel med VPN</string> <string name="we_will_look_into_this">Vi kommer att undersöka detta.</string> <string name="wireguard_custon_port_title">Anpassad</string> diff --git a/android/lib/resource/src/main/res/values-th/strings.xml b/android/lib/resource/src/main/res/values-th/strings.xml index 9664d0a917..f91d3ed15c 100644 --- a/android/lib/resource/src/main/res/values-th/strings.xml +++ b/android/lib/resource/src/main/res/values-th/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">ตำแหน่งที่ตั้งทั้งหมด</string> <string name="all_providers">ผู้ให้บริการทั้งหมด</string> <string name="always_on_vpn_error_notification_content">ไม่สามารถเริ่มการเชื่อมต่ออุโมงค์ได้ โปรดปิดใช้งาน Always-on VPN เป็นเวลา <b>%1$s</b> ก่อนที่จะใช้งาน Mullvad VPN</string> - <string name="always_on_vpn_error_notification_title">Always-on VPN ได้รับการมอบหมายไปยังแอปอื่นแล้ว</string> <string name="any">อะไรก็ได้</string> <string name="api_access_description">จัดการและเพิ่มวิธีแบบกำหนดเอง เพื่อเข้าถึง Mullvad API</string> <string name="api_access_method_info_first_line">แอปจำเป็นต้องสื่อสารกับเซิร์ฟเวอร์ Mullvad API เพื่อนำคุณเข้าสู่ระบบ ดึงข้อมูลรายการเซิร์ฟเวอร์ และการดำเนินการที่สำคัญอื่นๆ</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">ป้อนรหัสบัตรกำนัล</string> <string name="entry">เข้า</string> <string name="error_occurred">เกิดข้อผิดพลาดขึ้น</string> - <string name="error_state">ไม่สามารถเชื่อมต่ออย่างปลอดภัยได้</string> <string name="exclude_applications">แอปพลิเคชันที่แยกออก</string> <string name="exit">ออก</string> <string name="failed_to_block_internet">ไม่สามารถบล็อกการรับส่งข้อมูลทางเครือข่ายทั้งหมดได้ โปรดแก้ไขปัญหาหรือส่งรายงานปัญหา</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">รหัสบัตรกำนัลไม่ถูกต้อง</string> <string name="ipv6_unavailable">ไม่สามารถกำหนดค่า IPv6 ได้ โปรดปิดใช้งานรายการนี้ในแอป หรือเปิดใช้งานบนอุปกรณ์ของคุณ</string> <string name="is_offline">อุปกรณ์ของคุณออฟไลน์อยู่ อุโมงค์จะเชื่อมต่อโดยอัตโนมัติ หลังจากที่อุปกรณ์ของคุณกลับมาออนไลน์</string> + <string name="legacy_always_on_vpn_error_notification_title">Always-on VPN ได้รับการมอบหมายไปยังแอปอื่นแล้ว</string> <string name="less_than_a_day_left">เหลือเวลาน้อยกว่าหนึ่งวัน</string> <string name="less_than_one_day">น้อยกว่าหนึ่งวัน</string> <string name="list_name">ชื่อรายการ</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">ดูเหมือนว่า คุณได้ป้อนหมายเลขบัญชีแทนรหัสบัตรกำนัล หากคุณต้องการเปลี่ยนบัญชีที่ใช้งานอยู่ โปรดออกจากระบบก่อน</string> <string name="voucher_success_title">แลกบัตรกำนัลสำเร็จแล้ว</string> <string name="vpn_permission_denied_error">การให้สิทธิ์ VPN ถูกปฏิเสธ ในขณะที่สร้างอุโมงค์ โปรดลองเชื่อมต่อใหม่อีกครั้ง</string> - <string name="vpn_permission_error_notification_message">Always-on VPN อาจได้รับการเปิดใช้งานสำหรับแอปอื่น</string> <string name="vpn_permission_error_notification_title">เกิดข้อผิดพลาดในการอนุญาต VPN</string> <string name="we_will_look_into_this">เราจะตรวจสอบปัญหานี้</string> <string name="wireguard_custon_port_title">กำหนดเอง</string> diff --git a/android/lib/resource/src/main/res/values-tr/strings.xml b/android/lib/resource/src/main/res/values-tr/strings.xml index cdb01b9d2f..2f7e1d1dad 100644 --- a/android/lib/resource/src/main/res/values-tr/strings.xml +++ b/android/lib/resource/src/main/res/values-tr/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">Tüm konumlar</string> <string name="all_providers">Tüm hizmet sağlayıcılar</string> <string name="always_on_vpn_error_notification_content">Tünel bağlantısı başlatılamıyor. Mullvad VPN\'i kullanmadan önce lütfen Her zaman açık VPN\'i <b>%1$s</b> için devre dışı bırakın.</string> - <string name="always_on_vpn_error_notification_title">Her zaman açık VPN başka bir uygulamaya atandı</string> <string name="any">Tümü</string> <string name="api_access_description">Mullvad API\'sine erişim için özel yöntemler ekleyip yönetin.</string> <string name="api_access_method_info_first_line">Uygulamanın oturumunuzu açmak, sunucu listelerini almak ve diğer kritik işlemleri yapmak için bir Mullvad API sunucusuyla iletişim kurması gerekir.</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">Kupon kodunu girin</string> <string name="entry">Giriş</string> <string name="error_occurred">Bir hata oluştu.</string> - <string name="error_state">GÜVENLİ BAĞLANTI OLUŞTURULAMADI</string> <string name="exclude_applications">Hariç tutulan uygulamalar</string> <string name="exit">Çıkış</string> <string name="failed_to_block_internet">Tüm ağ trafiği engellenemiyor. Lütfen sorunu çözmeyi deneyin veya bir hata raporu gönderin.</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">Kupon kodu geçersiz.</string> <string name="ipv6_unavailable">IPv6 yapılandırılamadı. IPv6\'yı uygulamada devre dışı bırakın veya cihazınızda etkinleştirin.</string> <string name="is_offline">Cihazınız çevrimdışı. Cihazınız tekrar çevrimiçi olduğunda tünel otomatik olarak bağlanacak.</string> + <string name="legacy_always_on_vpn_error_notification_title">Her zaman açık VPN başka bir uygulamaya atandı</string> <string name="less_than_a_day_left">bir günden az kaldı</string> <string name="less_than_one_day">bir günden az</string> <string name="list_name">Liste adı</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">Kupon kodu yerine hesap numarası girdiniz. Aktif hesabı değiştirmek istiyorsanız lütfen önce çıkış yapın.</string> <string name="voucher_success_title">Kupon başarıyla kullanıldı.</string> <string name="vpn_permission_denied_error">Tünel oluşturulurken VPN izni reddedildi. Lütfen tekrar bağlanmayı deneyin.</string> - <string name="vpn_permission_error_notification_message">Her zaman açık VPN başka bir uygulama için etkinleştirilebilir</string> <string name="vpn_permission_error_notification_title">VPN izin hatası</string> <string name="we_will_look_into_this">Bunu araştıracağız.</string> <string name="wireguard_custon_port_title">Özel</string> diff --git a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml index 39306a64ae..f9c87e9191 100644 --- a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">所有位置</string> <string name="all_providers">所有提供商</string> <string name="always_on_vpn_error_notification_content">无法启动隧道连接。在使用 Mullvad VPN 之前,请为 <b>%1$s</b> 禁用“始终开启 VPN”。</string> - <string name="always_on_vpn_error_notification_title">“始终开启 VPN”已分配给其他应用</string> <string name="any">任何</string> <string name="api_access_description">管理和添加访问 Mulvad API 的自定义方法。</string> <string name="api_access_method_info_first_line">该应用需要与 Mulvad API 服务器通信,以便您登录、获取服务器列表和执行其他关键操作。</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">输入优惠码</string> <string name="entry">入口</string> <string name="error_occurred">出错了。</string> - <string name="error_state">无法保护连接</string> <string name="exclude_applications">排除的应用程序</string> <string name="exit">出口</string> <string name="failed_to_block_internet">无法阻止所有网络流量。请排查问题或发送问题报告。</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">该优惠券码无效。</string> <string name="ipv6_unavailable">无法配置 IPv6。请在应用中将其禁用或在您的设备上将其启用。</string> <string name="is_offline">您的设备已离线。在您的设备重新上线后,隧道将自动连接。</string> + <string name="legacy_always_on_vpn_error_notification_title">“始终开启 VPN”已分配给其他应用</string> <string name="less_than_a_day_left">剩余时间不足 1 天</string> <string name="less_than_one_day">不足 1 天</string> <string name="list_name">列表名称</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">您输入的似乎是帐号,而不是代金券码。如果您想更改有效帐户,请先退出登录。</string> <string name="voucher_success_title">优惠券已成功兑换。</string> <string name="vpn_permission_denied_error">创建隧道时,VPN 权限被拒绝。请尝试重新连接。</string> - <string name="vpn_permission_error_notification_message">可能为另一个应用启用了“始终开启 VPN”</string> <string name="vpn_permission_error_notification_title">VPN 权限错误</string> <string name="we_will_look_into_this">我们将对此进行调查。</string> <string name="wireguard_custon_port_title">自定义</string> diff --git a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml index 8be3c88b59..6cd3b5d8fc 100644 --- a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml @@ -22,7 +22,6 @@ <string name="all_locations">所有位置</string> <string name="all_providers">所有供應商</string> <string name="always_on_vpn_error_notification_content">無法啟動通道連線。在使用 Mullvad VPN 之前,請先為 <b>%1$s</b> 停用「始終啟用 VPN」。</string> - <string name="always_on_vpn_error_notification_title">「始終啟用 VPN」已指派給其他應用程式</string> <string name="any">任何</string> <string name="api_access_description">管理並新增自訂方式以存取 Mullvad API。</string> <string name="api_access_method_info_first_line">該應用程式需要與 Mulvad API 伺服器通訊,以便您登入、取得伺服器清單並執行其他重要作業。</string> @@ -134,7 +133,6 @@ <string name="enter_voucher_code">輸入優惠券兌換碼</string> <string name="entry">入口</string> <string name="error_occurred">發生錯誤了。</string> - <string name="error_state">保護連線失敗</string> <string name="exclude_applications">已排除的應用程式</string> <string name="exit">出口</string> <string name="failed_to_block_internet">無法封鎖所有網路流量。請排除故障或傳送問題回報。</string> @@ -170,6 +168,7 @@ <string name="invalid_voucher">憑證兌換碼無效。</string> <string name="ipv6_unavailable">無法配置 IPv6。請在應用程式中將其停用,或是在您的裝置上啟用它。</string> <string name="is_offline">您的裝置已離線。在您的裝置重新上線後,通道將自動連線。</string> + <string name="legacy_always_on_vpn_error_notification_title">「始終啟用 VPN」已指派給其他應用程式</string> <string name="less_than_a_day_left">剩餘時間不足 1 天</string> <string name="less_than_one_day">不足 1 天</string> <string name="list_name">清單名稱</string> @@ -334,7 +333,6 @@ <string name="voucher_is_account_number">您輸入的似乎是帳戶,而不是憑證代碼。如果您想變更有效帳戶,請先登出。</string> <string name="voucher_success_title">憑證已成功兌換。</string> <string name="vpn_permission_denied_error">建立通道時,VPN 權限被拒絕。請嘗試重新連線。</string> - <string name="vpn_permission_error_notification_message">可能已為另一個應用程式啟用了「始終啟用 VPN」</string> <string name="vpn_permission_error_notification_title">VPN 權限錯誤</string> <string name="we_will_look_into_this">我們會對此進行調查。</string> <string name="wireguard_custon_port_title">自訂</string> diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml index 4625fb3b5f..fc2cf0cce1 100644 --- a/android/lib/resource/src/main/res/values/strings.xml +++ b/android/lib/resource/src/main/res/values/strings.xml @@ -105,7 +105,7 @@ <string name="edit_message">Edit message</string> <string name="try_again">Try again</string> <string name="blocked_connection">BLOCKED CONNECTION</string> - <string name="error_state">FAILED TO SECURE CONNECTION</string> + <string name="error_state">FAILED TO CONNECT</string> <string name="connected">Connected</string> <string name="cancel">Cancel</string> <string name="disconnect">Disconnect</string> @@ -164,12 +164,15 @@ <string name="hide_account_number">Hide account number</string> <string name="failed_to_remove_device">Failed to remove device</string> <string name="changes_dialog_subtitle">Changes in this version:</string> - <string name="always_on_vpn_error_notification_title">Always-on VPN assigned to other app</string> + <string name="always_on_vpn_error_notification_title">Always-on VPN assigned to %s</string> <string name="always_on_vpn_error_notification_content"> <![CDATA[Unable to start tunnel connection. Please disable Always-on VPN for <b>%s</b> before using Mullvad VPN.]]> </string> + <string name="legacy_always_on_vpn_error_notification_title">Always-on VPN assigned to other app</string> + <string name="legacy_always_on_vpn_error_notification_content">Unable to start tunnel connection. Please disable Always-on VPN before using Mullvad VPN.</string> <string name="vpn_permission_error_notification_title">VPN permission error</string> - <string name="vpn_permission_error_notification_message">Always-on VPN might be enabled for another app</string> + <string name="vpn_permission_error_notification_message">Please press connect to request VPN permission</string> + <string name="new_device_notification_title">NEW DEVICE CREATED</string> <string name="new_device_notification_message"> <![CDATA[Welcome, this device is now called <b>%s</b>. For more details see the info button in Account.]]> diff --git a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxy.kt b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxy.kt index 2dbd15ec03..08a0a517f0 100644 --- a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxy.kt +++ b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxy.kt @@ -2,7 +2,6 @@ package net.mullvad.mullvadvpn.lib.shared import arrow.core.Either import arrow.core.raise.either -import arrow.core.raise.ensure import kotlinx.coroutines.flow.combine import net.mullvad.mullvadvpn.lib.daemon.grpc.ManagementService import net.mullvad.mullvadvpn.lib.model.ConnectError @@ -12,7 +11,7 @@ import net.mullvad.mullvadvpn.lib.model.TunnelState class ConnectionProxy( private val managementService: ManagementService, translationRepository: RelayLocationTranslationRepository, - private val vpnPermissionRepository: VpnPermissionRepository, + private val vpnProfileUseCase: VpnProfileUseCase, ) { val tunnelState = combine(managementService.tunnelState, translationRepository.translations) { @@ -35,7 +34,7 @@ class ConnectionProxy( copy(city = translations[city] ?: city, country = translations[country] ?: country) suspend fun connect(): Either<ConnectError, Boolean> = either { - ensure(vpnPermissionRepository.hasVpnPermission()) { ConnectError.NoVpnPermission } + vpnProfileUseCase.prepareVpn().mapLeft(ConnectError::NotPrepared).bind() managementService.connect().bind() } diff --git a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/VpnPermissionRepository.kt b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/VpnPermissionRepository.kt deleted file mode 100644 index b97c60316c..0000000000 --- a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/VpnPermissionRepository.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.mullvad.mullvadvpn.lib.shared - -import android.content.Context -import android.net.VpnService -import net.mullvad.mullvadvpn.lib.common.util.getAlwaysOnVpnAppName - -class VpnPermissionRepository(private val applicationContext: Context) { - fun hasVpnPermission(): Boolean = VpnService.prepare(applicationContext) == null - - fun getAlwaysOnVpnAppName() = applicationContext.getAlwaysOnVpnAppName() -} diff --git a/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/VpnProfileUseCase.kt b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/VpnProfileUseCase.kt new file mode 100644 index 0000000000..cebac0be04 --- /dev/null +++ b/android/lib/shared/src/main/kotlin/net/mullvad/mullvadvpn/lib/shared/VpnProfileUseCase.kt @@ -0,0 +1,11 @@ +package net.mullvad.mullvadvpn.lib.shared + +import android.content.Context +import arrow.core.Either +import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe +import net.mullvad.mullvadvpn.lib.model.PrepareError +import net.mullvad.mullvadvpn.lib.model.Prepared + +class VpnProfileUseCase(private val applicationContext: Context) { + fun prepareVpn(): Either<PrepareError, Prepared> = applicationContext.prepareVpnSafe() +} diff --git a/android/lib/shared/src/test/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxyTest.kt b/android/lib/shared/src/test/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxyTest.kt index 0652867105..24a14ce782 100644 --- a/android/lib/shared/src/test/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxyTest.kt +++ b/android/lib/shared/src/test/kotlin/net/mullvad/mullvadvpn/lib/shared/ConnectionProxyTest.kt @@ -1,31 +1,36 @@ package net.mullvad.mullvadvpn.lib.shared +import android.content.Intent +import arrow.core.left +import arrow.core.right import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.mockk.unmockkAll import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.lib.daemon.grpc.ManagementService +import net.mullvad.mullvadvpn.lib.model.PrepareError +import net.mullvad.mullvadvpn.lib.model.Prepared import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test class ConnectionProxyTest { private val mockManagementService: ManagementService = mockk(relaxed = true) - private val mockVpnPermissionRepository: VpnPermissionRepository = mockk() + private val mockVpnPermissionRepository: VpnProfileUseCase = mockk() private val mockTranslationRepository: RelayLocationTranslationRepository = mockk(relaxed = true) private val connectionProxy: ConnectionProxy = ConnectionProxy( managementService = mockManagementService, - vpnPermissionRepository = mockVpnPermissionRepository, + vpnProfileUseCase = mockVpnPermissionRepository, translationRepository = mockTranslationRepository, ) @Test fun `connect with vpn permission allowed should call managementService connect`() = runTest { - every { mockVpnPermissionRepository.hasVpnPermission() } returns true + every { mockVpnPermissionRepository.prepareVpn() } returns Prepared.right() connectionProxy.connect() coVerify(exactly = 1) { mockManagementService.connect() } } @@ -33,7 +38,8 @@ class ConnectionProxyTest { @Test fun `connect with vpn permission not allowed should not call managementService connect`() = runTest { - every { mockVpnPermissionRepository.hasVpnPermission() } returns false + every { mockVpnPermissionRepository.prepareVpn() } returns + PrepareError.NotPrepared(Intent()).left() connectionProxy.connect() coVerify(exactly = 0) { mockManagementService.connect() } } diff --git a/android/lib/talpid/build.gradle.kts b/android/lib/talpid/build.gradle.kts index c53c2add28..24ba625ff2 100644 --- a/android/lib/talpid/build.gradle.kts +++ b/android/lib/talpid/build.gradle.kts @@ -30,9 +30,11 @@ android { dependencies { implementation(projects.lib.model) + implementation(projects.lib.common) implementation(libs.androidx.ktx) implementation(libs.androidx.lifecycle.service) + implementation(libs.arrow) implementation(libs.kermit) implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) 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 dc1f8d23ca..74d44005cd 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 @@ -10,6 +10,8 @@ import java.net.Inet4Address import java.net.Inet6Address import java.net.InetAddress import kotlin.properties.Delegates.observable +import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe +import net.mullvad.mullvadvpn.lib.model.PrepareError import net.mullvad.talpid.model.CreateTunResult import net.mullvad.talpid.model.TunConfig import net.mullvad.talpid.util.TalpidSdkUtils.setMeteredIfSupported @@ -76,10 +78,11 @@ open class TalpidVpnService : LifecycleVpnService() { // Function is to be cleaned up and lint suppression to be removed. @Suppress("ReturnCount") private fun createTun(config: TunConfig): CreateTunResult { - if (prepare(this) != null) { - // VPN permission wasn't granted - return CreateTunResult.PermissionDenied - } + prepareVpnSafe() + .mapLeft { it.toCreateTunResult() } + .onLeft { + return it + } val invalidDnsServerAddresses = ArrayList<InetAddress>() @@ -149,6 +152,13 @@ open class TalpidVpnService : LifecycleVpnService() { return protect(socket) } + private fun PrepareError.toCreateTunResult() = + when (this) { + is PrepareError.OtherLegacyAlwaysOnVpn -> CreateTunResult.OtherLegacyAlwaysOnVpn + is PrepareError.NotPrepared -> CreateTunResult.NotPrepared + is PrepareError.OtherAlwaysOnApp -> CreateTunResult.OtherAlwaysOnApp(appName) + } + private fun InetAddress.prefixLength(): Int = when (this) { is Inet4Address -> IPV4_PREFIX_LENGTH diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/CreateTunResult.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/CreateTunResult.kt index 089112e3ab..3cd73685f7 100644 --- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/CreateTunResult.kt +++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/model/CreateTunResult.kt @@ -17,7 +17,13 @@ sealed class CreateTunResult { get() = true } - data object PermissionDenied : CreateTunResult() - + // Establish error data object TunnelDeviceError : CreateTunResult() + + // Prepare errors + data object OtherLegacyAlwaysOnVpn : CreateTunResult() + + data class OtherAlwaysOnApp(val appName: String) : CreateTunResult() + + data object NotPrepared : CreateTunResult() } diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/InetAddressExt.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/InetAddressExt.kt deleted file mode 100644 index d310deb884..0000000000 --- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/util/InetAddressExt.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.mullvad.talpid.util - -import java.net.InetAddress - -fun InetAddress.addressString(): String { - val hostNameAndAddress = this.toString().split('/', limit = 2) - val address = hostNameAndAddress[1] - - return address -} diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt index 5745377254..cf324e6023 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt @@ -2,9 +2,9 @@ package net.mullvad.mullvadvpn.service.notifications import android.app.Service import android.content.pm.ServiceInfo -import android.net.VpnService import android.os.Build import co.touchlab.kermit.Logger +import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe import net.mullvad.mullvadvpn.lib.model.Notification import net.mullvad.mullvadvpn.lib.model.NotificationChannel import net.mullvad.mullvadvpn.lib.model.NotificationTunnelState @@ -40,7 +40,7 @@ class ForegroundNotificationManager( private fun notifyForeground(tunnelStateNotification: Notification.Tunnel) { val androidNotification = tunnelStateNotification.toNotification(vpnService) - if (VpnService.prepare(vpnService) != null) { + if (vpnService.prepareVpnSafe().isLeft()) { // Got connect/disconnect intent, but we don't have permission to go in foreground. // tunnel state will return permission and we will eventually get stopped by system. Logger.i("Did not start foreground: VPN permission not granted") @@ -65,7 +65,7 @@ class ForegroundNotificationManager( private val defaultNotification = Notification.Tunnel( NotificationChannel.TunnelUpdates.id, - NotificationTunnelState.Disconnected(true), + NotificationTunnelState.Disconnected(null), emptyList(), false, ) diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt index c66839ddc5..f836cbcd1b 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt @@ -6,18 +6,19 @@ import android.content.Intent import androidx.core.app.NotificationCompat import net.mullvad.mullvadvpn.lib.common.constant.KEY_CONNECT_ACTION import net.mullvad.mullvadvpn.lib.common.constant.KEY_DISCONNECT_ACTION -import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PERMISSION +import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PROFILE import net.mullvad.mullvadvpn.lib.common.constant.MAIN_ACTIVITY_CLASS import net.mullvad.mullvadvpn.lib.common.util.SdkUtils import net.mullvad.mullvadvpn.lib.model.Notification import net.mullvad.mullvadvpn.lib.model.NotificationAction import net.mullvad.mullvadvpn.lib.model.NotificationTunnelState +import net.mullvad.mullvadvpn.lib.model.PrepareError import net.mullvad.mullvadvpn.service.R internal fun Notification.Tunnel.toNotification(context: Context) = NotificationCompat.Builder(context, channelId.value) .setContentIntent(contentIntent(context)) - .setContentTitle(context.getString(state.contentTitleResourceId())) + .setContentTitle(state.contentTitleResourceId(context)) .setSmallIcon(R.drawable.small_logo_white) .apply { actions.forEach { addAction(it.toCompatAction(context)) } } .setOngoing(ongoing) @@ -35,37 +36,41 @@ private fun Notification.Tunnel.contentIntent(context: Context): PendingIntent { return PendingIntent.getActivity(context, 1, intent, SdkUtils.getSupportedPendingIntentFlags()) } -private fun NotificationTunnelState.contentTitleResourceId(): Int = +private fun NotificationTunnelState.contentTitleResourceId(context: Context): String = when (this) { - NotificationTunnelState.Connected -> R.string.connected - NotificationTunnelState.Connecting -> R.string.connecting + NotificationTunnelState.Connected -> context.getString(R.string.connected) + NotificationTunnelState.Connecting -> context.getString(R.string.connecting) is NotificationTunnelState.Disconnected -> { - if (this.hasVpnPermission) { - R.string.disconnected - } else { - R.string.disconnected_vpn_permission_error + when (prepareError) { + is PrepareError.NotPrepared -> + context.getString(R.string.disconnected_vpn_permission_error) + else -> context.getString(R.string.disconnected) } } - NotificationTunnelState.Disconnecting -> R.string.disconnecting - NotificationTunnelState.Reconnecting -> R.string.reconnecting - NotificationTunnelState.Error.Blocking -> R.string.blocking_internet - is NotificationTunnelState.Error.Critical -> R.string.critical_error - NotificationTunnelState.Error.DeviceOffline -> R.string.blocking_internet_device_offline + NotificationTunnelState.Disconnecting -> context.getString(R.string.disconnecting) + NotificationTunnelState.Reconnecting -> context.getString(R.string.reconnecting) + NotificationTunnelState.Error.Blocking -> context.getString(R.string.blocking_internet) + is NotificationTunnelState.Error.Critical -> context.getString(R.string.critical_error) + NotificationTunnelState.Error.DeviceOffline -> + context.getString(R.string.blocking_internet_device_offline) NotificationTunnelState.Error.VpnPermissionDenied -> - R.string.vpn_permission_error_notification_title - NotificationTunnelState.Error.AlwaysOnVpn -> R.string.always_on_vpn_error_notification_title + context.getString(R.string.vpn_permission_error_notification_title) + is NotificationTunnelState.Error.AlwaysOnVpn -> + context.getString(R.string.always_on_vpn_error_notification_title, appName) + NotificationTunnelState.Error.LegacyLockdown -> + context.getString(R.string.legacy_always_on_vpn_error_notification_title) } internal fun NotificationAction.Tunnel.toCompatAction(context: Context): NotificationCompat.Action { val pendingIntent = - if (this is NotificationAction.Tunnel.RequestPermission) { + if (this is NotificationAction.Tunnel.RequestVpnProfile) { val intent = Intent().apply { setClassName(context.packageName, MAIN_ACTIVITY_CLASS) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - setAction(KEY_REQUEST_VPN_PERMISSION) + setAction(KEY_REQUEST_VPN_PROFILE) } PendingIntent.getActivity(context, 1, intent, SdkUtils.getSupportedPendingIntentFlags()) @@ -85,7 +90,7 @@ fun NotificationAction.Tunnel.titleResource() = when (this) { NotificationAction.Tunnel.Cancel -> R.string.cancel NotificationAction.Tunnel.Connect, - NotificationAction.Tunnel.RequestPermission -> R.string.connect + is NotificationAction.Tunnel.RequestVpnProfile -> R.string.connect NotificationAction.Tunnel.Disconnect -> R.string.disconnect NotificationAction.Tunnel.Dismiss -> R.string.dismiss } @@ -93,7 +98,7 @@ fun NotificationAction.Tunnel.titleResource() = fun NotificationAction.Tunnel.toKey() = when (this) { NotificationAction.Tunnel.Connect -> KEY_CONNECT_ACTION - NotificationAction.Tunnel.RequestPermission -> KEY_REQUEST_VPN_PERMISSION + is NotificationAction.Tunnel.RequestVpnProfile -> KEY_REQUEST_VPN_PROFILE NotificationAction.Tunnel.Cancel, NotificationAction.Tunnel.Disconnect, NotificationAction.Tunnel.Dismiss -> KEY_DISCONNECT_ACTION diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt index 2068f1adff..e9c52bba48 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt @@ -19,15 +19,16 @@ import net.mullvad.mullvadvpn.lib.model.NotificationChannelId import net.mullvad.mullvadvpn.lib.model.NotificationId import net.mullvad.mullvadvpn.lib.model.NotificationTunnelState import net.mullvad.mullvadvpn.lib.model.NotificationUpdate +import net.mullvad.mullvadvpn.lib.model.PrepareError import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy import net.mullvad.mullvadvpn.lib.shared.DeviceRepository -import net.mullvad.mullvadvpn.lib.shared.VpnPermissionRepository +import net.mullvad.mullvadvpn.lib.shared.VpnProfileUseCase import net.mullvad.mullvadvpn.service.notifications.NotificationProvider class TunnelStateNotificationProvider( connectionProxy: ConnectionProxy, - vpnPermissionRepository: VpnPermissionRepository, + vpnPermissionRepository: VpnProfileUseCase, deviceRepository: DeviceRepository, channelId: NotificationChannelId, scope: CoroutineScope, @@ -49,8 +50,7 @@ class TunnelStateNotificationProvider( tunnelState( tunnelState, actionAfterDisconnect, - vpnPermissionRepository.hasVpnPermission(), - vpnPermissionRepository.getAlwaysOnVpnAppName(), + vpnPermissionRepository.prepareVpn().leftOrNull(), ) return@combine NotificationUpdate.Notify( @@ -68,14 +68,9 @@ class TunnelStateNotificationProvider( private fun tunnelState( tunnelState: TunnelState, actionAfterDisconnect: ActionAfterDisconnect?, - hasVpnPermission: Boolean, - alwaysOnVpnPermissionName: String?, + prepareError: PrepareError?, ): NotificationTunnelState = - tunnelState.toNotificationTunnelState( - actionAfterDisconnect, - hasVpnPermission, - alwaysOnVpnPermissionName, - ) + tunnelState.toNotificationTunnelState(actionAfterDisconnect, prepareError) private fun Flow<TunnelState>.actionAfterDisconnect(): Flow<ActionAfterDisconnect?> = filterIsInstance<TunnelState.Disconnecting>() @@ -84,11 +79,10 @@ class TunnelStateNotificationProvider( private fun TunnelState.toNotificationTunnelState( actionAfterDisconnect: ActionAfterDisconnect?, - hasVpnPermission: Boolean, - alwaysOnVpnPermissionName: String?, + prepareError: PrepareError?, ) = when (this) { - is TunnelState.Disconnected -> NotificationTunnelState.Disconnected(hasVpnPermission) + is TunnelState.Disconnected -> NotificationTunnelState.Disconnected(prepareError) is TunnelState.Connecting -> { if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { NotificationTunnelState.Reconnecting @@ -104,20 +98,21 @@ class TunnelStateNotificationProvider( } } is TunnelState.Connected -> NotificationTunnelState.Connected - is TunnelState.Error -> toNotificationTunnelState(alwaysOnVpnPermissionName) + is TunnelState.Error -> toNotificationTunnelState() } - private fun TunnelState.Error.toNotificationTunnelState( - alwaysOnVpnPermissionName: String? - ): NotificationTunnelState.Error { + private fun TunnelState.Error.toNotificationTunnelState(): NotificationTunnelState.Error { val cause = errorState.cause return when { cause is ErrorStateCause.IsOffline && errorState.isBlocking -> NotificationTunnelState.Error.DeviceOffline cause is ErrorStateCause.InvalidDnsServers -> NotificationTunnelState.Error.Blocking - cause is ErrorStateCause.VpnPermissionDenied -> - alwaysOnVpnPermissionName?.let { NotificationTunnelState.Error.AlwaysOnVpn } - ?: NotificationTunnelState.Error.VpnPermissionDenied + cause is ErrorStateCause.OtherLegacyAlwaysOnApp -> + NotificationTunnelState.Error.LegacyLockdown + cause is ErrorStateCause.NotPrepared -> + NotificationTunnelState.Error.VpnPermissionDenied + cause is ErrorStateCause.OtherAlwaysOnApp -> + NotificationTunnelState.Error.AlwaysOnVpn(cause.appName) errorState.isBlocking -> NotificationTunnelState.Error.Blocking else -> NotificationTunnelState.Error.Critical } @@ -126,10 +121,11 @@ class TunnelStateNotificationProvider( private fun NotificationTunnelState.toAction(): NotificationAction.Tunnel = when (this) { is NotificationTunnelState.Disconnected -> { - if (this.hasVpnPermission) { - NotificationAction.Tunnel.Connect - } else { - NotificationAction.Tunnel.RequestPermission + when (prepareError) { + is PrepareError.OtherAlwaysOnApp, + is PrepareError.OtherLegacyAlwaysOnVpn, + null -> NotificationAction.Tunnel.Connect + is PrepareError.NotPrepared -> NotificationAction.Tunnel.RequestVpnProfile } } NotificationTunnelState.Disconnecting -> NotificationAction.Tunnel.Connect @@ -140,6 +136,7 @@ class TunnelStateNotificationProvider( is NotificationTunnelState.Error.Critical, NotificationTunnelState.Error.DeviceOffline, NotificationTunnelState.Error.VpnPermissionDenied, - NotificationTunnelState.Error.AlwaysOnVpn -> NotificationAction.Tunnel.Dismiss + is NotificationTunnelState.Error.AlwaysOnVpn, + NotificationTunnelState.Error.LegacyLockdown -> NotificationAction.Tunnel.Dismiss } } diff --git a/android/tile/build.gradle.kts b/android/tile/build.gradle.kts index b1c558685c..816c7de883 100644 --- a/android/tile/build.gradle.kts +++ b/android/tile/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(libs.koin.android) implementation(libs.androidx.appcompat) + implementation(libs.arrow) implementation(libs.kermit) implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) diff --git a/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt b/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt index b1fa9ae311..9fe3c06dfc 100644 --- a/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt +++ b/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.app.PendingIntent import android.content.Intent import android.graphics.drawable.Icon -import android.net.VpnService import android.os.Build import android.service.quicksettings.Tile import android.service.quicksettings.TileService @@ -26,6 +25,7 @@ import net.mullvad.mullvadvpn.lib.common.constant.MAIN_ACTIVITY_CLASS import net.mullvad.mullvadvpn.lib.common.constant.VPN_SERVICE_CLASS import net.mullvad.mullvadvpn.lib.common.util.SdkUtils import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.setSubtitleIfSupported +import net.mullvad.mullvadvpn.lib.common.util.prepareVpnSafe import net.mullvad.mullvadvpn.lib.daemon.grpc.GrpcConnectivityState import net.mullvad.mullvadvpn.lib.daemon.grpc.ManagementService import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect @@ -88,9 +88,26 @@ class MullvadTileService : TileService() { @SuppressLint("StartActivityAndCollapseDeprecated") private fun toggleTunnel() { - val isSetup = VpnService.prepare(applicationContext) == null + val isSetup = applicationContext.prepareVpnSafe().isRight() // TODO This logic should be more advanced, we should ensure user has an account setup etc. - if (!isSetup) { + if (isSetup) { + Logger.i("TileService: VPN service is setup") + + val intent = + Intent().apply { + setClassName(applicationContext.packageName, VPN_SERVICE_CLASS) + action = + if (qsTile.state == Tile.STATE_INACTIVE) { + KEY_CONNECT_ACTION + } else { + KEY_DISCONNECT_ACTION + } + } + + // Always start as foreground, e.g if app is dead we won't be allowed to start if not + // in foreground. + startForegroundService(intent) + } else { Logger.i("TileService: VPN service not setup, starting main activity") val intent = @@ -103,24 +120,7 @@ class MullvadTileService : TileService() { action = Intent.ACTION_MAIN } startActivityAndCollapseCompat(intent) - return - } else { - Logger.i("TileService: VPN service is setup") } - val intent = - Intent().apply { - setClassName(applicationContext.packageName, VPN_SERVICE_CLASS) - action = - if (qsTile.state == Tile.STATE_INACTIVE) { - KEY_CONNECT_ACTION - } else { - KEY_DISCONNECT_ACTION - } - } - - // Always start as foreground, e.g if app is dead we won't be allowed to start if not - // in foreground. - startForegroundService(intent) } @SuppressLint("StartActivityAndCollapseDeprecated") |
