summaryrefslogtreecommitdiffhomepage
path: root/android/service
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2024-11-18 14:23:05 +0100
committerDavid Göransson <david.goransson@mullvad.net>2024-11-27 09:00:18 +0100
commit1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988 (patch)
treea83565926fb753a2dd9fd24f0e2bd07262e4507e /android/service
parent0d155385e1cb7075012bd270de0398d83a438bc5 (diff)
downloadmullvadvpn-1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988.tar.xz
mullvadvpn-1bb7fc7ebaa2837ed9f9d28c2bb5a6fd91033988.zip
Handle legacy always-on vpn profiles
Co-authored-by: Jonatan Rhodin <jonatan.rhodin@mullvad.net>
Diffstat (limited to 'android/service')
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/ForegroundNotificationManager.kt6
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt45
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt49
3 files changed, 51 insertions, 49 deletions
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
}
}