diff options
8 files changed, 53 insertions, 134 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b29bbfcfb..5a7cbad3c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Line wrap the file at 100 chars. Th - Fix request to connect from notification or quick-settings tile not connecting if VPN permission isn't granted to the app. The app will now show the UI to ask for the permission and correctly connect after it is granted. +- Fix quick-settings tile sometimes showing the wrong tunnel state. ## [2021.3] - 2021-04-28 diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 1515835794..66edb7dc33 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -68,7 +68,8 @@ <service android:name="net.mullvad.mullvadvpn.service.MullvadTileService" android:label="@string/app_name" android:icon="@drawable/small_logo_black" - android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> + android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" + android:process=":mullvadvpn_tile"> <intent-filter> <action android:name="android.service.quicksettings.action.QS_TILE" /> </intent-filter> 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 dfd3213811..db9662f5d6 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt @@ -1,31 +1,52 @@ package net.mullvad.mullvadvpn.service +import android.content.ComponentName import android.content.Intent import android.graphics.drawable.Icon import android.os.Build +import android.os.IBinder +import android.os.Messenger import android.service.quicksettings.Tile import android.service.quicksettings.TileService import kotlin.properties.Delegates.observable import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.TunnelState -import net.mullvad.mullvadvpn.service.tunnelstate.TunnelStateListener +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection import net.mullvad.talpid.tunnel.ActionAfterDisconnect class MullvadTileService : TileService() { + private val serviceConnectionManager = object : android.content.ServiceConnection { + override fun onServiceConnected(className: ComponentName, binder: IBinder) { + serviceConnection = ServiceConnection(Messenger(binder)) + } + + override fun onServiceDisconnected(className: ComponentName) { + serviceConnection = null + } + } + + private var serviceConnection by observable<ServiceConnection?>( + null + ) { _, oldConnection, newConnection -> + oldConnection?.onDestroy() + + newConnection?.connectionProxy?.run { + onStateChange.subscribe(this@MullvadTileService, ::updateTunnelState) + } + } + private var secured by observable(false) { _, wasSecured, isSecured -> if (wasSecured != isSecured) { updateTileState() } } - private lateinit var listener: TunnelStateListener private lateinit var securedIcon: Icon private lateinit var unsecuredIcon: Icon override fun onCreate() { super.onCreate() - listener = TunnelStateListener(this) securedIcon = Icon.createWithResource(this, R.drawable.small_logo_white) unsecuredIcon = Icon.createWithResource(this, R.drawable.small_logo_black) } @@ -33,19 +54,9 @@ class MullvadTileService : TileService() { override fun onStartListening() { super.onStartListening() - listener.onStateChange = { state -> - secured = when (state) { - is TunnelState.Disconnected -> false - is TunnelState.Connecting -> true - is TunnelState.Connected -> true - is TunnelState.Disconnecting -> { - state.actionAfterDisconnect == ActionAfterDisconnect.Reconnect - } - is TunnelState.Error -> { - state.errorState.isBlocking - } - } - } + val intent = Intent(this, MullvadVpnService::class.java) + + bindService(intent, serviceConnectionManager, BIND_IMPORTANT) updateTileState() } @@ -69,9 +80,22 @@ class MullvadTileService : TileService() { } override fun onStopListening() { - listener.onStateChange = null + unbindService(serviceConnectionManager) + serviceConnection = null - super.onStartListening() + super.onStopListening() + } + + 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 + } + is TunnelState.Error -> tunnelState.errorState.isBlocking + } } private fun updateTileState() { 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 a4685b6dd8..177db1d712 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -15,7 +15,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.tunnelstate.TunnelStateUpdater import net.mullvad.mullvadvpn.ui.MainActivity import net.mullvad.talpid.TalpidVpnService @@ -55,7 +54,6 @@ class MullvadVpnService : TalpidVpnService() { private lateinit var endpoint: ServiceEndpoint private lateinit var keyguardManager: KeyguardManager private lateinit var notificationManager: ForegroundNotificationManager - private lateinit var tunnelStateUpdater: TunnelStateUpdater private var pendingAction by observable<PendingAction?>(null) { _, _, _ -> endpoint.settingsListener.settings?.let { settings -> @@ -91,8 +89,6 @@ class MullvadVpnService : TalpidVpnService() { connectionProxy.reconnect() } - tunnelStateUpdater = TunnelStateUpdater(this, connectionProxy) - notificationManager = ForegroundNotificationManager(this, connectionProxy, keyguardManager).apply { acknowledgeStartForegroundService() @@ -112,6 +108,12 @@ class MullvadVpnService : TalpidVpnService() { start() } + + // Remove any leftover tunnel state persistence data + getSharedPreferences("tunnel_state", MODE_PRIVATE) + .edit() + .clear() + .commit() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt deleted file mode 100644 index 73b5c6de7e..0000000000 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt +++ /dev/null @@ -1,59 +0,0 @@ -package net.mullvad.mullvadvpn.service.tunnelstate - -import android.content.Context -import android.content.SharedPreferences.OnSharedPreferenceChangeListener -import java.net.InetSocketAddress -import net.mullvad.mullvadvpn.model.TunnelState -import net.mullvad.talpid.net.Endpoint -import net.mullvad.talpid.net.TransportProtocol -import net.mullvad.talpid.net.TunnelEndpoint - -private const val SHARED_PREFERENCES = "tunnel_state" -private const val KEY_TUNNEL_STATE = "tunnel_state" - -// TODO: Maybe replace using this with actually persisting the endpoint information -private val dummyTunnelEndpoint = TunnelEndpoint( - Endpoint( - InetSocketAddress.createUnresolved("dummy", 53), - TransportProtocol.Tcp - ) -) - -internal class Persistence(context: Context) { - val sharedPreferences = - context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE) - - var state - get() = loadState() - set(value) { - persistState(value) - } - - var listener: OnSharedPreferenceChangeListener? = null - set(value) { - if (value != field) { - if (field != null) { - sharedPreferences.unregisterOnSharedPreferenceChangeListener(field) - } - - if (value != null) { - sharedPreferences.registerOnSharedPreferenceChangeListener(value) - } - - field = value - } - } - - private fun loadState(): TunnelState { - val description = sharedPreferences.getString(KEY_TUNNEL_STATE, TunnelState.DISCONNECTED)!! - - return TunnelState.fromString(description, dummyTunnelEndpoint) - } - - private fun persistState(state: TunnelState) { - sharedPreferences - .edit() - .putString(KEY_TUNNEL_STATE, state.toString()) - .commit() - } -} diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt deleted file mode 100644 index 7f7832d3e4..0000000000 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt +++ /dev/null @@ -1,36 +0,0 @@ -package net.mullvad.mullvadvpn.service.tunnelstate - -import android.content.Context -import android.content.SharedPreferences -import android.content.SharedPreferences.OnSharedPreferenceChangeListener -import net.mullvad.mullvadvpn.model.TunnelState - -class TunnelStateListener(context: Context) { - private val persistence = Persistence(context) - - private val listener = object : OnSharedPreferenceChangeListener { - override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) { - state = persistence.state - } - } - - var state = persistence.state - private set(value) { - if (field != value) { - field = value - onStateChange?.invoke(value) - } - } - - var onStateChange: ((TunnelState) -> Unit)? = null - set(value) { - field = value - - if (value == null) { - persistence.listener = null - } else { - persistence.listener = listener - state = persistence.state - } - } -} 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 deleted file mode 100644 index 4d33ec4896..0000000000 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.mullvad.mullvadvpn.service.tunnelstate - -import android.content.Context -import net.mullvad.mullvadvpn.service.endpoint.ConnectionProxy - -class TunnelStateUpdater(context: Context, private val connectionProxy: ConnectionProxy) { - private val persistence = Persistence(context) - - init { - connectionProxy.onStateChange.subscribe(this) { newState -> - persistence.state = newState - } - } -} 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 655a26f2d8..861a54a561 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 @@ -21,7 +21,7 @@ import org.koin.core.scope.get @OptIn(KoinApiExtension::class) class ServiceConnection( connection: Messenger, - onServiceReady: (ServiceConnection) -> Unit + onServiceReady: ((ServiceConnection) -> Unit)? = null ) : KoinScopeComponent { private val dispatcher = DispatchingHandler(Looper.getMainLooper()) { message -> Event.fromMessage(message) @@ -48,7 +48,7 @@ class ServiceConnection( init { dispatcher.registerHandler(Event.ListenerReady::class) { _ -> - onServiceReady(this@ServiceConnection) + onServiceReady?.invoke(this@ServiceConnection) } registerListener(connection) |
