diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2021-04-12 10:55:12 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2021-04-12 10:55:12 -0300 |
| commit | 7c79849b2f6b98889be9c0e07cf03da8bdd611f3 (patch) | |
| tree | ea5adfa3f5f5eb94ddc50c3504cbdb32e4d39c99 /android/src | |
| parent | 90a50180746139c07f8aea0a8922f415006a05a1 (diff) | |
| parent | c751fd82c709347aa6dfe782f77bfa797e21037b (diff) | |
| download | mullvadvpn-7c79849b2f6b98889be9c0e07cf03da8bdd611f3.tar.xz mullvadvpn-7c79849b2f6b98889be9c0e07cf03da8bdd611f3.zip | |
Merge branch 'split-connection-proxy'
Diffstat (limited to 'android/src')
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() } |
