summaryrefslogtreecommitdiffhomepage
path: root/android/src
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2021-04-12 10:55:12 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2021-04-12 10:55:12 -0300
commit7c79849b2f6b98889be9c0e07cf03da8bdd611f3 (patch)
treeea5adfa3f5f5eb94ddc50c3504cbdb32e4d39c99 /android/src
parent90a50180746139c07f8aea0a8922f415006a05a1 (diff)
parentc751fd82c709347aa6dfe782f77bfa797e21037b (diff)
downloadmullvadvpn-7c79849b2f6b98889be9c0e07cf03da8bdd611f3.tar.xz
mullvadvpn-7c79849b2f6b98889be9c0e07cf03da8bdd611f3.zip
Merge branch 'split-connection-proxy'
Diffstat (limited to 'android/src')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt4
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt12
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt33
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt21
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt7
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt37
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt84
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt11
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt10
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt38
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt23
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt9
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/TunnelStateNotification.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt (renamed from android/src/main/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxy.kt)109
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VpnPermission.kt10
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/net/Endpoint.kt5
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt6
-rw-r--r--android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt21
24 files changed, 291 insertions, 179 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt
index 285b7abea2..1211e88d04 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt
@@ -6,6 +6,7 @@ import net.mullvad.mullvadvpn.model.GeoIpLocation
import net.mullvad.mullvadvpn.model.KeygenEvent
import net.mullvad.mullvadvpn.model.LoginStatus as LoginStatusData
import net.mullvad.mullvadvpn.model.Settings
+import net.mullvad.mullvadvpn.model.TunnelState
// Events that can be sent from the service
sealed class Event : Message.EventMessage() {
@@ -30,6 +31,9 @@ sealed class Event : Message.EventMessage() {
data class SplitTunnelingUpdate(val excludedApps: List<String>?) : Event()
@Parcelize
+ data class TunnelStateChange(val tunnelState: TunnelState) : Event()
+
+ @Parcelize
data class WireGuardKeyStatus(val keyStatus: KeygenEvent?) : Event()
companion object {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt
index b8dfc3c3dd..1937ce424b 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt
@@ -10,9 +10,15 @@ sealed class Request : Message.RequestMessage() {
protected override val messageKey = MESSAGE_KEY
@Parcelize
+ object Connect : Request()
+
+ @Parcelize
object CreateAccount : Request()
@Parcelize
+ object Disconnect : Request()
+
+ @Parcelize
data class ExcludeApp(val packageName: String) : Request()
@Parcelize
@@ -34,6 +40,9 @@ sealed class Request : Message.RequestMessage() {
object PersistExcludedApps : Request()
@Parcelize
+ object Reconnect : Request()
+
+ @Parcelize
data class RegisterListener(val listener: Messenger) : Request()
@Parcelize
@@ -43,6 +52,9 @@ sealed class Request : Message.RequestMessage() {
data class SetEnableSplitTunneling(val enable: Boolean) : Request()
@Parcelize
+ data class VpnPermissionResponse(val isGranted: Boolean) : Request()
+
+ @Parcelize
object WireGuardGenerateKey : Request()
@Parcelize
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt
index dc5698b17a..918396a263 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt
@@ -1,16 +1,35 @@
package net.mullvad.mullvadvpn.model
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
import net.mullvad.talpid.net.TunnelEndpoint
import net.mullvad.talpid.tunnel.ActionAfterDisconnect
import net.mullvad.talpid.tunnel.ErrorState
import net.mullvad.talpid.tunnel.ErrorStateCause
-sealed class TunnelState() {
- object Disconnected : TunnelState()
- class Connecting(val endpoint: TunnelEndpoint?, val location: GeoIpLocation?) : TunnelState()
- class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) : TunnelState()
- class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : TunnelState()
- class Error(val errorState: ErrorState) : TunnelState()
+sealed class TunnelState() : Parcelable {
+ @Parcelize
+ object Disconnected : TunnelState(), Parcelable
+
+ @Parcelize
+ class Connecting(
+ val endpoint: TunnelEndpoint?,
+ val location: GeoIpLocation?
+ ) : TunnelState(), Parcelable
+
+ @Parcelize
+ class Connected(
+ val endpoint: TunnelEndpoint,
+ val location: GeoIpLocation?
+ ) : TunnelState(), Parcelable
+
+ @Parcelize
+ class Disconnecting(
+ val actionAfterDisconnect: ActionAfterDisconnect
+ ) : TunnelState(), Parcelable
+
+ @Parcelize
+ class Error(val errorState: ErrorState) : TunnelState(), Parcelable
companion object {
const val DISCONNECTED = "disconnected"
@@ -37,7 +56,7 @@ sealed class TunnelState() {
}
}
- override fun toString() = when (this) {
+ override fun toString(): String = when (this) {
is TunnelState.Disconnected -> DISCONNECTED
is TunnelState.Connecting -> CONNECTING
is TunnelState.Connected -> CONNECTED
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt
index 7138e0ebae..f1221fa307 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt
@@ -14,13 +14,13 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.channels.sendBlocking
import net.mullvad.mullvadvpn.model.TunnelState
+import net.mullvad.mullvadvpn.service.endpoint.ConnectionProxy
import net.mullvad.mullvadvpn.service.notifications.TunnelStateNotification
-import net.mullvad.talpid.util.EventNotifier
import net.mullvad.talpid.util.autoSubscribable
class ForegroundNotificationManager(
val service: MullvadVpnService,
- val serviceNotifier: EventNotifier<ServiceInstance?>,
+ val connectionProxy: ConnectionProxy,
val keyguardManager: KeyguardManager
) {
private sealed class UpdaterMessage {
@@ -43,13 +43,6 @@ class ForegroundNotificationManager(
}
}
- private var tunnelStateEvents by autoSubscribable<TunnelState>(
- this,
- TunnelState.Disconnected
- ) { newState ->
- updater.sendBlocking(UpdaterMessage.NewTunnelState(newState))
- }
-
private var deviceIsUnlocked by observable(!keyguardManager.isDeviceLocked) { _, _, _ ->
updater.sendBlocking(UpdaterMessage.UpdateAction())
}
@@ -59,7 +52,7 @@ class ForegroundNotificationManager(
}
private val tunnelState
- get() = tunnelStateEvents?.latestEvent ?: TunnelState.Disconnected
+ get() = connectionProxy.onStateChange.latestEvent
private val shouldBeOnForeground
get() = lockedToForeground || !(tunnelState is TunnelState.Disconnected)
@@ -76,8 +69,8 @@ class ForegroundNotificationManager(
}
init {
- serviceNotifier.subscribe(this) { newServiceInstance ->
- tunnelStateEvents = newServiceInstance?.connectionProxy?.onStateChange
+ connectionProxy.onStateChange.subscribe(this) { newState ->
+ updater.sendBlocking(UpdaterMessage.NewTunnelState(newState))
}
service.apply {
@@ -94,11 +87,9 @@ class ForegroundNotificationManager(
}
fun onDestroy() {
- serviceNotifier.unsubscribe(this)
-
accountNumberEvents = null
- tunnelStateEvents = null
+ connectionProxy.onStateChange.unsubscribe(this)
service.unregisterReceiver(deviceLockListener)
updater.close()
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
index 3f85bd58e0..bab7bcf3ce 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
@@ -17,11 +17,11 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
protected var daemonInterfaceAddress = 0L
val onSettingsChange = EventNotifier<Settings?>(null)
+ var onTunnelStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected)
var onAppVersionInfoChange: ((AppVersionInfo) -> Unit)? = null
var onKeygenEvent: ((KeygenEvent) -> Unit)? = null
var onRelayListChange: ((RelayList) -> Unit)? = null
- var onTunnelStateChange: ((TunnelState) -> Unit)? = null
var onDaemonStopped: (() -> Unit)? = null
init {
@@ -29,6 +29,7 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
initialize(vpnService, vpnService.cacheDir.absolutePath, vpnService.filesDir.absolutePath)
onSettingsChange.notify(getSettings())
+ onTunnelStateChange.notify(getState() ?: TunnelState.Disconnected)
}
fun connect() {
@@ -133,11 +134,11 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
fun onDestroy() {
onSettingsChange.unsubscribeAll()
+ onTunnelStateChange.unsubscribeAll()
onAppVersionInfoChange = null
onKeygenEvent = null
onRelayListChange = null
- onTunnelStateChange = null
onDaemonStopped = null
deinitialize()
@@ -205,7 +206,7 @@ class MullvadDaemon(val vpnService: MullvadVpnService) {
}
private fun notifyTunnelStateEvent(event: TunnelState) {
- onTunnelStateChange?.invoke(event)
+ onTunnelStateChange.notify(event)
}
private fun notifyDaemonStopped() {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
index bca3f4956d..1d379c5775 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
@@ -16,7 +16,6 @@ import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.model.Settings
import net.mullvad.mullvadvpn.service.endpoint.ServiceEndpoint
import net.mullvad.mullvadvpn.service.notifications.AccountExpiryNotification
-import net.mullvad.mullvadvpn.service.persistence.SplitTunnelingPersistence
import net.mullvad.mullvadvpn.service.tunnelstate.TunnelStateUpdater
import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.talpid.TalpidVpnService
@@ -49,6 +48,9 @@ class MullvadVpnService : TalpidVpnService() {
private val binder = LocalBinder()
private val serviceNotifier = EventNotifier<ServiceInstance?>(null)
+ private val connectionProxy
+ get() = endpoint.connectionProxy
+
private var state = State.Running
private var setUpDaemonJob: Job? = null
@@ -77,13 +79,11 @@ class MullvadVpnService : TalpidVpnService() {
private lateinit var tunnelStateUpdater: TunnelStateUpdater
private var pendingAction by observable<PendingAction?>(null) { _, _, _ ->
- val connectionProxy = instance?.connectionProxy
-
// The service instance awaits the split tunneling initialization, which also starts the
// endpoint. So if the instance is not null, the endpoint has certainly been initialized.
- if (connectionProxy != null) {
+ if (instance != null) {
endpoint.settingsListener.settings?.let { settings ->
- handlePendingAction(connectionProxy, settings)
+ handlePendingAction(settings)
}
}
}
@@ -102,17 +102,24 @@ class MullvadVpnService : TalpidVpnService() {
daemonInstance = DaemonInstance(this)
keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
- tunnelStateUpdater = TunnelStateUpdater(this, serviceNotifier)
endpoint = ServiceEndpoint(
Looper.getMainLooper(),
daemonInstance.intermittentDaemon,
connectivityListener,
- SplitTunnelingPersistence(this)
+ this
)
+ endpoint.splitTunneling.onChange.subscribe(this@MullvadVpnService) { excludedApps ->
+ disallowedApps = excludedApps
+ markTunAsStale()
+ connectionProxy.reconnect()
+ }
+
+ tunnelStateUpdater = TunnelStateUpdater(this, connectionProxy)
+
notificationManager =
- ForegroundNotificationManager(this, serviceNotifier, keyguardManager).apply {
+ ForegroundNotificationManager(this, connectionProxy, keyguardManager).apply {
acknowledgeStartForegroundService()
accountNumberEvents = endpoint.settingsListener.accountNumberNotifier
}
@@ -230,25 +237,15 @@ class MullvadVpnService : TalpidVpnService() {
}
private suspend fun setUpInstance(daemon: MullvadDaemon, settings: Settings) {
- val connectionProxy = ConnectionProxy(this, daemon)
val customDns = CustomDns(daemon, endpoint.settingsListener)
- endpoint.splitTunneling.onChange.subscribe(this@MullvadVpnService) { excludedApps ->
- disallowedApps = excludedApps
- markTunAsStale()
- connectionProxy.reconnect()
- }
-
- handlePendingAction(connectionProxy, settings)
-
- endpoint.locationInfoCache.stateEvents = connectionProxy.onStateChange
+ handlePendingAction(settings)
if (state == State.Running) {
instance = ServiceInstance(
endpoint.messenger,
daemon,
daemonInstance.intermittentDaemon,
- connectionProxy,
customDns
)
}
@@ -276,7 +273,7 @@ class MullvadVpnService : TalpidVpnService() {
}
}
- private fun handlePendingAction(connectionProxy: ConnectionProxy, settings: Settings) {
+ private fun handlePendingAction(settings: Settings) {
when (pendingAction) {
PendingAction.Connect -> {
if (settings.accountToken != null) {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt
index f97d8c870f..f4e898d143 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ServiceInstance.kt
@@ -7,11 +7,9 @@ class ServiceInstance(
val messenger: Messenger,
val daemon: MullvadDaemon,
val intermittentDaemon: Intermittent<MullvadDaemon>,
- val connectionProxy: ConnectionProxy,
val customDns: CustomDns,
) {
fun onDestroy() {
- connectionProxy.onDestroy()
customDns.onDestroy()
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt
new file mode 100644
index 0000000000..94cc9f05b8
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt
@@ -0,0 +1,84 @@
+package net.mullvad.mullvadvpn.service.endpoint
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.ClosedReceiveChannelException
+import kotlinx.coroutines.channels.actor
+import kotlinx.coroutines.channels.sendBlocking
+import net.mullvad.mullvadvpn.ipc.Event
+import net.mullvad.mullvadvpn.ipc.Request
+import net.mullvad.mullvadvpn.model.TunnelState
+import net.mullvad.talpid.util.EventNotifier
+
+class ConnectionProxy(val vpnPermission: VpnPermission, endpoint: ServiceEndpoint) {
+ private enum class Command {
+ CONNECT,
+ RECONNECT,
+ DISCONNECT,
+ }
+
+ private val commandChannel = spawnActor()
+ private val daemon = endpoint.intermittentDaemon
+ private val initialState = TunnelState.Disconnected
+
+ var onStateChange = EventNotifier<TunnelState>(initialState)
+
+ var state by onStateChange.notifiable()
+ private set
+
+ init {
+ daemon.registerListener(this) { newDaemon ->
+ newDaemon?.onTunnelStateChange?.subscribe(this@ConnectionProxy) { newState ->
+ state = newState
+ }
+ }
+
+ onStateChange.subscribe(this) { tunnelState ->
+ endpoint.sendEvent(Event.TunnelStateChange(tunnelState))
+ }
+
+ endpoint.dispatcher.apply {
+ registerHandler(Request.Connect::class) { _ -> connect() }
+ registerHandler(Request.Reconnect::class) { _ -> reconnect() }
+ registerHandler(Request.Disconnect::class) { _ -> disconnect() }
+ }
+ }
+
+ fun connect() {
+ commandChannel.sendBlocking(Command.CONNECT)
+ }
+
+ fun reconnect() {
+ commandChannel.sendBlocking(Command.RECONNECT)
+ }
+
+ fun disconnect() {
+ commandChannel.sendBlocking(Command.DISCONNECT)
+ }
+
+ fun onDestroy() {
+ commandChannel.close()
+ onStateChange.unsubscribeAll()
+ daemon.unregisterListener(this)
+ }
+
+ private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) {
+ try {
+ while (true) {
+ val command = channel.receive()
+
+ when (command) {
+ Command.CONNECT -> {
+ vpnPermission.request()
+ daemon.await().connect()
+ }
+ Command.RECONNECT -> daemon.await().reconnect()
+ Command.DISCONNECT -> daemon.await().disconnect()
+ }
+ }
+ } catch (exception: ClosedReceiveChannelException) {
+ // Closed sender, so stop the actor
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt
index 2f118ede6d..df769abad9 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt
@@ -20,7 +20,6 @@ import net.mullvad.mullvadvpn.model.RelaySettings
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.util.ExponentialBackoff
import net.mullvad.talpid.tunnel.ActionAfterDisconnect
-import net.mullvad.talpid.util.autoSubscribable
class LocationInfoCache(private val endpoint: ServiceEndpoint) {
companion object {
@@ -70,11 +69,11 @@ class LocationInfoCache(private val endpoint: ServiceEndpoint) {
}
}
- var stateEvents by autoSubscribable<TunnelState>(this, TunnelState.Disconnected) { newState ->
- state = newState
- }
-
init {
+ endpoint.connectionProxy.onStateChange.subscribe(this) { newState ->
+ state = newState
+ }
+
endpoint.connectivityListener.connectivityNotifier.subscribe(this) { isConnected ->
if (isConnected && state is TunnelState.Disconnected) {
fetchRequestChannel.sendBlocking(RequestFetch.ForRealLocation)
@@ -85,9 +84,9 @@ class LocationInfoCache(private val endpoint: ServiceEndpoint) {
}
fun onDestroy() {
+ endpoint.connectionProxy.onStateChange.unsubscribe(this)
endpoint.connectivityListener.connectivityNotifier.unsubscribe(this)
endpoint.settingsListener.relaySettingsNotifier.unsubscribe(this)
- stateEvents = null
fetchRequestChannel.close()
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt
index 8e11e1e1cd..1d7ee77763 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.service.endpoint
+import android.content.Context
import android.os.DeadObjectException
import android.os.Looper
import android.os.Messenger
@@ -22,7 +23,7 @@ class ServiceEndpoint(
looper: Looper,
internal val intermittentDaemon: Intermittent<MullvadDaemon>,
val connectivityListener: ConnectivityListener,
- splitTunnelingPersistence: SplitTunnelingPersistence
+ context: Context
) {
private val listeners = mutableSetOf<Messenger>()
private val registrationQueue: SendChannel<Messenger> = startRegistrator()
@@ -33,12 +34,15 @@ class ServiceEndpoint(
val messenger = Messenger(dispatcher)
+ val vpnPermission = VpnPermission(context, this)
+
+ val connectionProxy = ConnectionProxy(vpnPermission, this)
val settingsListener = SettingsListener(this)
val accountCache = AccountCache(this)
val keyStatusListener = KeyStatusListener(this)
val locationInfoCache = LocationInfoCache(this)
- val splitTunneling = SplitTunneling(splitTunnelingPersistence, this)
+ val splitTunneling = SplitTunneling(SplitTunnelingPersistence(context), this)
init {
dispatcher.registerHandler(Request.RegisterListener::class) { request ->
@@ -51,6 +55,7 @@ class ServiceEndpoint(
registrationQueue.close()
accountCache.onDestroy()
+ connectionProxy.onDestroy()
keyStatusListener.onDestroy()
locationInfoCache.onDestroy()
settingsListener.onDestroy()
@@ -95,6 +100,7 @@ class ServiceEndpoint(
listeners.add(listener)
val initialEvents = listOf(
+ Event.TunnelStateChange(connectionProxy.state),
Event.LoginStatus(accountCache.onLoginStatusChange.latestEvent),
Event.AccountHistory(accountCache.onAccountHistoryChange.latestEvent),
Event.SettingsUpdate(settingsListener.settings),
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt
new file mode 100644
index 0000000000..50b5a606d4
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt
@@ -0,0 +1,38 @@
+package net.mullvad.mullvadvpn.service.endpoint
+
+import android.content.Context
+import android.content.Intent
+import android.net.VpnService
+import net.mullvad.mullvadvpn.ipc.Request
+import net.mullvad.mullvadvpn.ui.MainActivity
+import net.mullvad.mullvadvpn.util.Intermittent
+
+class VpnPermission(private val context: Context, endpoint: ServiceEndpoint) {
+ private val isGranted = Intermittent<Boolean>()
+
+ init {
+ endpoint.dispatcher.registerHandler(Request.VpnPermissionResponse::class) { request ->
+ isGranted.spawnUpdate(request.isGranted)
+ }
+ }
+
+ suspend fun request(): Boolean {
+ val intent = VpnService.prepare(context)
+
+ if (intent == null) {
+ isGranted.update(true)
+ } else {
+ val activityIntent = Intent(context, MainActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ putExtra(MainActivity.KEY_SHOULD_CONNECT, true)
+ }
+
+ isGranted.update(null)
+
+ context.startActivity(activityIntent)
+ }
+
+ return isGranted.await()
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt
index 638a30d333..4d33ec4896 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt
@@ -1,29 +1,14 @@
package net.mullvad.mullvadvpn.service.tunnelstate
import android.content.Context
-import net.mullvad.mullvadvpn.service.ConnectionProxy
-import net.mullvad.mullvadvpn.service.ServiceInstance
-import net.mullvad.talpid.util.EventNotifier
+import net.mullvad.mullvadvpn.service.endpoint.ConnectionProxy
-class TunnelStateUpdater(context: Context, serviceNotifier: EventNotifier<ServiceInstance?>) {
+class TunnelStateUpdater(context: Context, private val connectionProxy: ConnectionProxy) {
private val persistence = Persistence(context)
- private var connectionProxy: ConnectionProxy? = null
- private var stateSubscriptionId: Int? = null
-
init {
- serviceNotifier.subscribe(this) { serviceInstance ->
- onNewServiceInstance(serviceInstance)
- }
- }
-
- private fun onNewServiceInstance(serviceInstance: ServiceInstance?) {
- connectionProxy?.onStateChange?.unsubscribe(this)
-
- connectionProxy = serviceInstance?.connectionProxy?.apply {
- onStateChange.subscribe(this@TunnelStateUpdater) { newState ->
- persistence.state = newState
- }
+ connectionProxy.onStateChange.subscribe(this) { newState ->
+ persistence.state = newState
}
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
index 486e1d156c..570475eb34 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
@@ -13,9 +13,6 @@ import android.view.WindowManager
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.BuildConfig
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.dataproxy.MullvadProblemReport
@@ -138,7 +135,7 @@ open class MainActivity : FragmentActivity() {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
- setVpnPermission(resultCode == Activity.RESULT_OK)
+ serviceConnection?.vpnPermission?.grant(resultCode == Activity.RESULT_OK)
}
override fun onBackPressed() {
@@ -229,8 +226,4 @@ open class MainActivity : FragmentActivity() {
commit()
}
}
-
- private fun setVpnPermission(allow: Boolean) = GlobalScope.launch(Dispatchers.Default) {
- serviceConnection?.connectionProxy?.vpnPermission?.complete(allow)
- }
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt
index 0cb33c3334..1927a0490f 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt
@@ -7,10 +7,10 @@ import android.view.ViewGroup
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.dataproxy.AppVersionInfoCache
import net.mullvad.mullvadvpn.dataproxy.RelayListListener
-import net.mullvad.mullvadvpn.service.ConnectionProxy
import net.mullvad.mullvadvpn.service.CustomDns
import net.mullvad.mullvadvpn.service.MullvadDaemon
import net.mullvad.mullvadvpn.ui.serviceconnection.AccountCache
+import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy
import net.mullvad.mullvadvpn.ui.serviceconnection.KeyStatusListener
import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/TunnelStateNotification.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/TunnelStateNotification.kt
index 80ae9da5e4..8c26c5dc1e 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/TunnelStateNotification.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/TunnelStateNotification.kt
@@ -3,7 +3,7 @@ package net.mullvad.mullvadvpn.ui.notification
import android.content.Context
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.model.TunnelState
-import net.mullvad.mullvadvpn.service.ConnectionProxy
+import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy
import net.mullvad.talpid.tunnel.ActionAfterDisconnect
import net.mullvad.talpid.tunnel.ErrorState
import net.mullvad.talpid.tunnel.ErrorStateCause
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxy.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt
index c21c4d54c6..35714d1ce5 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxy.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt
@@ -1,93 +1,66 @@
-package net.mullvad.mullvadvpn.service
+package net.mullvad.mullvadvpn.ui.serviceconnection
-import android.content.Context
-import android.content.Intent
-import android.net.VpnService
-import kotlinx.coroutines.CompletableDeferred
+import android.os.Messenger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.ipc.DispatchingHandler
+import net.mullvad.mullvadvpn.ipc.Event
+import net.mullvad.mullvadvpn.ipc.Request
import net.mullvad.mullvadvpn.model.TunnelState
-import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.talpid.tunnel.ActionAfterDisconnect
import net.mullvad.talpid.util.EventNotifier
val ANTICIPATED_STATE_TIMEOUT_MS = 1500L
-class ConnectionProxy(val context: Context, val daemon: MullvadDaemon) {
- var mainActivity: MainActivity? = null
-
- private var activeAction: Job? = null
+class ConnectionProxy(val connection: Messenger, eventDispatcher: DispatchingHandler<Event>) {
private var resetAnticipatedStateJob: Job? = null
- private val initialState: TunnelState = TunnelState.Disconnected
-
- var onStateChange = EventNotifier(initialState)
- var onUiStateChange = EventNotifier(initialState)
- var vpnPermission = CompletableDeferred<Boolean>()
+ val onStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected)
+ val onUiStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected)
var state by onStateChange.notifiable()
private set
var uiState by onUiStateChange.notifiable()
private set
- private val fetchInitialStateJob = fetchInitialState()
-
init {
- daemon.onTunnelStateChange = { newState ->
- synchronized(this) {
- resetAnticipatedStateJob?.cancel()
- state = newState
- uiState = newState
- }
+ eventDispatcher.registerHandler(Event.TunnelStateChange::class) { event ->
+ handleNewState(event.tunnelState)
}
}
fun connect() {
if (anticipateConnectingState()) {
- cancelActiveAction()
-
- requestVpnPermission()
-
- activeAction = GlobalScope.launch(Dispatchers.Default) {
- vpnPermission.await()
- daemon.connect()
- }
+ connection.send(Request.Connect.message)
}
}
- fun reconnect() {
+ fun disconnect() {
if (anticipateReconnectingState()) {
- cancelActiveAction()
- activeAction = GlobalScope.launch(Dispatchers.Default) {
- daemon.reconnect()
- }
+ connection.send(Request.Disconnect.message)
}
}
- fun disconnect() {
+ fun reconnect() {
if (anticipateDisconnectingState()) {
- cancelActiveAction()
- activeAction = GlobalScope.launch(Dispatchers.Default) {
- daemon.disconnect()
- }
+ connection.send(Request.Reconnect.message)
}
}
- fun cancelActiveAction() {
- activeAction?.cancel()
- }
-
fun onDestroy() {
- daemon.onTunnelStateChange = null
-
- onUiStateChange.unsubscribeAll()
onStateChange.unsubscribeAll()
+ onUiStateChange.unsubscribeAll()
+ }
- fetchInitialStateJob.cancel()
- cancelActiveAction()
+ private fun handleNewState(newState: TunnelState) {
+ synchronized(this) {
+ resetAnticipatedStateJob?.cancel()
+ state = newState
+ uiState = newState
+ }
}
private fun anticipateConnectingState(): Boolean {
@@ -163,40 +136,4 @@ class ConnectionProxy(val context: Context, val daemon: MullvadDaemon) {
currentJob = newJob
resetAnticipatedStateJob = newJob
}
-
- private fun requestVpnPermission() {
- val intent = VpnService.prepare(context)
-
- vpnPermission = CompletableDeferred()
-
- if (intent == null) {
- vpnPermission.complete(true)
- } else {
- val activity = mainActivity
-
- if (activity != null) {
- activity.requestVpnPermission(intent)
- } else {
- val activityIntent = Intent(context, MainActivity::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- putExtra(MainActivity.KEY_SHOULD_CONNECT, true)
- }
-
- uiState = state
-
- context.startActivity(activityIntent)
- }
- }
- }
-
- private fun fetchInitialState() = GlobalScope.launch(Dispatchers.Default) {
- val currentState = daemon.getState()
-
- synchronized(this) {
- if (state === initialState && currentState != null) {
- state = currentState
- }
- }
- }
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt
index becd03b203..6e2cee704f 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt
@@ -29,7 +29,7 @@ class ServiceConnection(private val service: ServiceInstance, mainActivity: Main
val daemon = service.daemon
val accountCache = AccountCache(service.messenger, dispatcher)
- val connectionProxy = service.connectionProxy
+ val connectionProxy = ConnectionProxy(service.messenger, dispatcher)
val customDns = service.customDns
val keyStatusListener = KeyStatusListener(service.messenger, dispatcher)
val locationInfoCache = LocationInfoCache(dispatcher)
@@ -37,13 +37,13 @@ class ServiceConnection(private val service: ServiceInstance, mainActivity: Main
val splitTunneling = get<SplitTunneling>(
parameters = { parametersOf(service.messenger, dispatcher) }
)
+ val vpnPermission = VpnPermission(service.messenger)
val appVersionInfoCache = AppVersionInfoCache(mainActivity, daemon, settingsListener)
var relayListListener = RelayListListener(daemon, settingsListener)
init {
appVersionInfoCache.onCreate()
- connectionProxy.mainActivity = mainActivity
registerListener()
}
@@ -51,13 +51,13 @@ class ServiceConnection(private val service: ServiceInstance, mainActivity: Main
dispatcher.onDestroy()
accountCache.onDestroy()
+ connectionProxy.onDestroy()
keyStatusListener.onDestroy()
locationInfoCache.onDestroy()
settingsListener.onDestroy()
appVersionInfoCache.onDestroy()
relayListListener.onDestroy()
- connectionProxy.mainActivity = null
}
private fun registerListener() {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VpnPermission.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VpnPermission.kt
new file mode 100644
index 0000000000..7983245eba
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VpnPermission.kt
@@ -0,0 +1,10 @@
+package net.mullvad.mullvadvpn.ui.serviceconnection
+
+import android.os.Messenger
+import net.mullvad.mullvadvpn.ipc.Request
+
+class VpnPermission(private val connection: Messenger) {
+ fun grant(isGranted: Boolean) {
+ connection.send(Request.VpnPermissionResponse(isGranted).message)
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/talpid/net/Endpoint.kt b/android/src/main/kotlin/net/mullvad/talpid/net/Endpoint.kt
index 7baeb66161..8937bd0122 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/net/Endpoint.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/net/Endpoint.kt
@@ -1,5 +1,8 @@
package net.mullvad.talpid.net
+import android.os.Parcelable
import java.net.InetSocketAddress
+import kotlinx.parcelize.Parcelize
-data class Endpoint(val address: InetSocketAddress, val protocol: TransportProtocol)
+@Parcelize
+data class Endpoint(val address: InetSocketAddress, val protocol: TransportProtocol) : Parcelable
diff --git a/android/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt b/android/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt
index 013399dc52..5efb1bcb1c 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt
@@ -1,5 +1,9 @@
package net.mullvad.talpid.net
-enum class TransportProtocol {
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+enum class TransportProtocol : Parcelable {
Tcp, Udp
}
diff --git a/android/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt b/android/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt
index 138ab57cc6..db9c2c4391 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt
@@ -1,3 +1,7 @@
package net.mullvad.talpid.net
-data class TunnelEndpoint(val endpoint: Endpoint)
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TunnelEndpoint(val endpoint: Endpoint) : Parcelable
diff --git a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt
index c20d5b33e4..365ac0811b 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt
@@ -1,5 +1,9 @@
package net.mullvad.talpid.tunnel
-enum class ActionAfterDisconnect {
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+enum class ActionAfterDisconnect : Parcelable {
Nothing, Block, Reconnect
}
diff --git a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt
index c88a932887..2c5ba00bf5 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt
@@ -1,3 +1,7 @@
package net.mullvad.talpid.tunnel
-data class ErrorState(val cause: ErrorStateCause, val isBlocking: Boolean)
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class ErrorState(val cause: ErrorStateCause, val isBlocking: Boolean) : Parcelable
diff --git a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt
index 8aa14dae1c..f5b79bdfd5 100644
--- a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt
+++ b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt
@@ -1,15 +1,34 @@
package net.mullvad.talpid.tunnel
+import android.os.Parcelable
import java.net.InetAddress
+import kotlinx.parcelize.Parcelize
-sealed class ErrorStateCause {
+sealed class ErrorStateCause : Parcelable {
+ @Parcelize
class AuthFailed(val reason: String?) : ErrorStateCause()
+
+ @Parcelize
object Ipv6Unavailable : ErrorStateCause()
+
+ @Parcelize
object SetFirewallPolicyError : ErrorStateCause()
+
+ @Parcelize
object SetDnsError : ErrorStateCause()
+
+ @Parcelize
class InvalidDnsServers(val addresses: ArrayList<InetAddress>) : ErrorStateCause()
+
+ @Parcelize
object StartTunnelError : ErrorStateCause()
+
+ @Parcelize
class TunnelParameterError(val error: ParameterGenerationError) : ErrorStateCause()
+
+ @Parcelize
object IsOffline : ErrorStateCause()
+
+ @Parcelize
object VpnPermissionDenied : ErrorStateCause()
}