summaryrefslogtreecommitdiffhomepage
path: root/android/src
diff options
context:
space:
mode:
Diffstat (limited to 'android/src')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt35
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt25
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt32
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt10
4 files changed, 79 insertions, 23 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt
index 751bcc9bc3..66ac88c91d 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt
@@ -7,21 +7,30 @@ import android.os.Looper
import android.os.Messenger
import kotlin.reflect.KClass
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.model.ServiceResult
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.service.MullvadVpnService
import net.mullvad.mullvadvpn.util.DispatchingFlow
import net.mullvad.mullvadvpn.util.bindServiceFlow
import net.mullvad.mullvadvpn.util.dispatchTo
+@FlowPreview
class ServiceConnection(context: Context, scope: CoroutineScope) {
private val activeListeners = MutableStateFlow<Pair<Messenger, Int>?>(null)
private val handler = HandlerFlow(Looper.getMainLooper(), Event::fromMessage)
@@ -30,9 +39,12 @@ class ServiceConnection(context: Context, scope: CoroutineScope) {
private lateinit var listenerRegistrations: StateFlow<Pair<Messenger, Int>?>
- lateinit var tunnelState: StateFlow<TunnelState>
+ lateinit var tunnelState: Flow<Pair<TunnelState, ServiceResult.ConnectionState>>
private set
+ private val serviceConnectionStateChannel =
+ Channel<ServiceResult.ConnectionState>(Channel.RENDEZVOUS)
+
init {
val dispatcher = handler
.filterNotNull()
@@ -41,11 +53,18 @@ class ServiceConnection(context: Context, scope: CoroutineScope) {
Pair(connection, listenerId)
}
- tunnelState = subscribeToState(
+ val tunnelStateEvents = subscribeToState(
Event.TunnelStateChange::class,
scope,
TunnelState.Disconnected
) { tunnelState }
+
+ tunnelState = tunnelStateEvents
+ .combine(
+ serviceConnectionStateChannel.consumeAsFlow()
+ ) { tunnelState, serviceConnectionState ->
+ tunnelState to serviceConnectionState
+ }
}
scope.launch { connect(context) }
@@ -57,10 +76,14 @@ class ServiceConnection(context: Context, scope: CoroutineScope) {
private suspend fun connect(context: Context) {
val intent = Intent(context, MullvadVpnService::class.java)
- context.bindServiceFlow(intent).collect { binder ->
- activeListeners.value = null
- binder?.let(::registerListener)
- }
+ context
+ .bindServiceFlow(intent)
+ .onStart { emit(ServiceResult.NOT_CONNECTED) }
+ .onEach { result -> serviceConnectionStateChannel.send(result.connectionState) }
+ .collect { result ->
+ activeListeners.value = null
+ result.binder?.let(::registerListener)
+ }
}
private fun registerListener(binder: IBinder) {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt
new file mode 100644
index 0000000000..b1d9f5be4c
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt
@@ -0,0 +1,25 @@
+package net.mullvad.mullvadvpn.model
+
+import android.os.IBinder
+
+data class ServiceResult(
+ val binder: IBinder?
+) {
+ enum class ConnectionState {
+ CONNECTED,
+ DISCONNECTED;
+ }
+
+ val connectionState: ConnectionState
+ get() {
+ return if (binder == null) {
+ ConnectionState.DISCONNECTED
+ } else {
+ ConnectionState.CONNECTED
+ }
+ }
+
+ companion object {
+ val NOT_CONNECTED = ServiceResult(null)
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt
index 434f6db8ee..4c15559912 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt
@@ -14,14 +14,13 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.ipc.ServiceConnection
+import net.mullvad.mullvadvpn.model.ServiceResult
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.talpid.tunnel.ActionAfterDisconnect
class MullvadTileService : TileService() {
- private var secured by observable(false) { _, wasSecured, isSecured ->
- if (wasSecured != isSecured) {
- updateTileState()
- }
+ private var secured by observable(false) { _, _, _ ->
+ updateTileState()
}
private lateinit var scope: CoroutineScope
@@ -68,18 +67,25 @@ class MullvadTileService : TileService() {
ServiceConnection(this@MullvadTileService, scope)
.tunnelState
.debounce(300L)
- .collect(::updateTunnelState)
+ .collect { updateTunnelState(it.first, it.second) }
}
- private fun updateTunnelState(tunnelState: TunnelState) {
- secured = when (tunnelState) {
- is TunnelState.Disconnected -> false
- is TunnelState.Connecting -> true
- is TunnelState.Connected -> true
- is TunnelState.Disconnecting -> {
- tunnelState.actionAfterDisconnect == ActionAfterDisconnect.Reconnect
+ private fun updateTunnelState(
+ tunnelState: TunnelState,
+ connectionState: ServiceResult.ConnectionState
+ ) {
+ secured = if (connectionState == ServiceResult.ConnectionState.CONNECTED) {
+ when (tunnelState) {
+ is TunnelState.Disconnected -> false
+ is TunnelState.Connecting -> true
+ is TunnelState.Connected -> true
+ is TunnelState.Disconnecting -> {
+ tunnelState.actionAfterDisconnect == ActionAfterDisconnect.Reconnect
+ }
+ is TunnelState.Error -> tunnelState.errorState.isBlocking
}
- is TunnelState.Error -> tunnelState.errorState.isBlocking
+ } else {
+ false
}
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt
index 71ce51a005..588f2ecfd1 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt
@@ -13,6 +13,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.take
+import net.mullvad.mullvadvpn.model.ServiceResult
fun <T> SendChannel<T>.safeOffer(element: T): Boolean {
return runCatching { offer(element) }.getOrDefault(false)
@@ -32,21 +33,22 @@ fun Animation.transitionFinished(): Flow<Unit> = callbackFlow<Unit> {
}
}.take(1)
-fun Context.bindServiceFlow(intent: Intent, flags: Int = 0): Flow<IBinder?> = callbackFlow {
+fun Context.bindServiceFlow(intent: Intent, flags: Int = 0): Flow<ServiceResult> = callbackFlow {
val connectionCallback = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
- safeOffer(binder)
+ safeOffer(ServiceResult(binder))
}
override fun onServiceDisconnected(className: ComponentName) {
- safeOffer(null)
+ safeOffer(ServiceResult.NOT_CONNECTED)
+ bindService(intent, this, flags)
}
}
bindService(intent, connectionCallback, flags)
awaitClose {
- safeOffer(null)
+ safeOffer(ServiceResult.NOT_CONNECTED)
Dispatchers.Default.dispatch(EmptyCoroutineContext) {
unbindService(connectionCallback)