diff options
Diffstat (limited to 'android/src')
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) |
