summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2022-03-29 14:53:02 +0200
committerAlbin <albin@mullvad.net>2022-03-30 09:55:00 +0200
commitcd954e5cfb8b1457bd5261e85fc78fc860ae5032 (patch)
treec5766920a3800f7926c3b93ec4448bab7a49d5cf /android
parent5c014d6c6ec9a5685be738eb084549cbdacd177b (diff)
downloadmullvadvpn-cd954e5cfb8b1457bd5261e85fc78fc860ae5032.tar.xz
mullvadvpn-cd954e5cfb8b1457bd5261e85fc78fc860ae5032.zip
Propagate device state over service connection
Adds device state communication between the app and service with a repository backed by a data source to add abstraction layers on the app side. Limitations: * Expiration is not updated correctly. * The login/creation flow has not been fully adapted to devices.
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt29
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt18
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt47
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt42
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt25
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AccountCache.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/DeviceRepository.kt20
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt27
16 files changed, 206 insertions, 51 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt
index a5854232a8..7fe971e678 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt
@@ -4,6 +4,7 @@ import android.os.Message as RawMessage
import android.os.Messenger
import kotlinx.parcelize.Parcelize
import net.mullvad.mullvadvpn.model.AppVersionInfo as AppVersionInfoData
+import net.mullvad.mullvadvpn.model.DeviceState
import net.mullvad.mullvadvpn.model.GeoIpLocation
import net.mullvad.mullvadvpn.model.KeygenEvent
import net.mullvad.mullvadvpn.model.LoginStatus as LoginStatusData
@@ -29,6 +30,9 @@ sealed class Event : Message.EventMessage() {
data class CurrentVersion(val version: String?) : Event()
@Parcelize
+ data class DeviceStateEvent(val newState: DeviceState) : Event()
+
+ @Parcelize
data class ListenerReady(val connection: Messenger, val listenerId: Int) : Event()
@Parcelize
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt
index 27a93b0a2d..78a6baa072 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt
@@ -42,6 +42,9 @@ sealed class Request : Message.RequestMessage() {
data class Login(val account: String?) : Request()
@Parcelize
+ object RefreshDeviceState : Request()
+
+ @Parcelize
object Logout : Request()
@Parcelize
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt
new file mode 100644
index 0000000000..04b87a87cd
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt
@@ -0,0 +1,29 @@
+package net.mullvad.mullvadvpn.model
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+sealed class DeviceState : Parcelable {
+ @Parcelize
+ object InitialState : DeviceState()
+
+ @Parcelize
+ data class DeviceRegistered(val deviceConfig: DeviceConfig) : DeviceState()
+
+ @Parcelize
+ object DeviceNotRegistered : DeviceState()
+
+ fun isInitialState(): Boolean {
+ return this is InitialState
+ }
+
+ fun token(): String? {
+ return (this as? DeviceRegistered)?.deviceConfig?.token
+ }
+
+ companion object {
+ fun fromDeviceConfig(deviceConfig: DeviceConfig?): DeviceState {
+ return deviceConfig?.let { DeviceRegistered(it) } ?: DeviceNotRegistered
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
index f4b7993e38..ea66d6b308 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt
@@ -1,9 +1,13 @@
package net.mullvad.mullvadvpn.service
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
import net.mullvad.mullvadvpn.model.AppVersionInfo
import net.mullvad.mullvadvpn.model.Device
import net.mullvad.mullvadvpn.model.DeviceConfig
import net.mullvad.mullvadvpn.model.DeviceEvent
+import net.mullvad.mullvadvpn.model.DeviceState
import net.mullvad.mullvadvpn.model.DnsOptions
import net.mullvad.mullvadvpn.model.GeoIpLocation
import net.mullvad.mullvadvpn.model.GetAccountDataResult
@@ -22,7 +26,6 @@ import net.mullvad.talpid.util.EventNotifier
class MullvadDaemon(vpnService: MullvadVpnService) {
protected var daemonInterfaceAddress = 0L
- var onDeviceChange = EventNotifier<DeviceConfig?>(null)
var onDeviceRemoved = EventNotifier<RemoveDeviceEvent?>(null)
val onSettingsChange = EventNotifier<Settings?>(null)
var onTunnelStateChange = EventNotifier<TunnelState>(TunnelState.Disconnected)
@@ -32,12 +35,17 @@ class MullvadDaemon(vpnService: MullvadVpnService) {
var onRelayListChange: ((RelayList) -> Unit)? = null
var onDaemonStopped: (() -> Unit)? = null
+ private val _deviceStateUpdates = MutableSharedFlow<DeviceState>(
+ extraBufferCapacity = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ val deviceStateUpdates = _deviceStateUpdates.asSharedFlow()
+
init {
System.loadLibrary("mullvad_jni")
initialize(vpnService, vpnService.cacheDir.absolutePath, vpnService.filesDir.absolutePath)
onSettingsChange.notify(getSettings())
- onDeviceChange.notify(getDevice())
onTunnelStateChange.notify(getState() ?: TunnelState.Disconnected)
}
@@ -120,8 +128,8 @@ class MullvadDaemon(vpnService: MullvadVpnService) {
fun getDevice(): DeviceConfig? = getDevice(daemonInterfaceAddress)
- fun removeDevice(accountToken: String?, device: Device) {
- removeDevice(daemonInterfaceAddress, accountToken, device.id)
+ fun removeDevice(accountToken: String, deviceId: String): RemoveDeviceResult {
+ return removeDevice(daemonInterfaceAddress, accountToken, deviceId)
}
fun setAllowLan(allowLan: Boolean) {
@@ -251,7 +259,7 @@ class MullvadDaemon(vpnService: MullvadVpnService) {
}
private fun notifyDeviceEvent(event: DeviceEvent) {
- onDeviceChange.notify(event.device)
+ _deviceStateUpdates.tryEmit(DeviceState.fromDeviceConfig(event.device))
}
private fun notifyRemoveDeviceEvent(event: RemoveDeviceEvent) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt
index 40fc8d7bd1..435c6bd220 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt
@@ -204,10 +204,8 @@ class AccountCache(private val endpoint: ServiceEndpoint) {
}
private suspend fun doLogout() {
- if (accountNumber != null) {
- daemon.await().logoutAccount()
- loginStatus = null
- }
+ daemon.await().logoutAccount()
+ loginStatus = null
}
private fun fetchAccountHistory() {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt
new file mode 100644
index 0000000000..dd0769c5e7
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt
@@ -0,0 +1,47 @@
+package net.mullvad.mullvadvpn.service.endpoint
+
+import kotlinx.coroutines.flow.collect
+import net.mullvad.mullvadvpn.ipc.Event
+import net.mullvad.mullvadvpn.ipc.Request
+import net.mullvad.mullvadvpn.model.DeviceState
+import net.mullvad.mullvadvpn.service.MullvadDaemon
+import net.mullvad.mullvadvpn.util.JobTracker
+
+class DaemonDeviceDataSource(
+ val endpoint: ServiceEndpoint
+) {
+ private val tracker = JobTracker()
+
+ init {
+ endpoint.intermittentDaemon.registerListener(this) { daemon ->
+ if (daemon != null) {
+ launchDeviceEndpointJobs(daemon)
+ } else {
+ tracker.cancelAllJobs()
+ }
+ }
+ }
+
+ private fun launchDeviceEndpointJobs(daemon: MullvadDaemon) {
+ tracker.newBackgroundJob("propagateDeviceUpdates") {
+ daemon.deviceStateUpdates.collect { newState ->
+ endpoint.sendEvent(Event.DeviceStateEvent(newState))
+ }
+ }
+
+ endpoint.dispatcher.registerHandler(Request.RefreshDeviceState::class) {
+ tracker.newBackgroundJob("refreshDeviceJob") {
+ daemon.getDevice()
+ .let { deviceConfig ->
+ Event.DeviceStateEvent(DeviceState.fromDeviceConfig(deviceConfig))
+ }
+ .also { event -> endpoint.sendEvent(event) }
+ }
+ }
+ }
+
+ fun onDestroy() {
+ tracker.cancelAllJobs()
+ endpoint.intermittentDaemon.unregisterListener(this)
+ }
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt
index 0a0c41b42e..f53454b401 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt
@@ -58,6 +58,8 @@ class ServiceEndpoint(
val splitTunneling = SplitTunneling(SplitTunnelingPersistence(context), this)
val voucherRedeemer = VoucherRedeemer(this)
+ private val deviceRepositoryBackend = DaemonDeviceDataSource(this)
+
init {
dispatcher.apply {
registerHandler(Request.RegisterListener::class) { request ->
@@ -79,6 +81,7 @@ class ServiceEndpoint(
authTokenCache.onDestroy()
connectionProxy.onDestroy()
customDns.onDestroy()
+ deviceRepositoryBackend.onDestroy()
keyStatusListener.onDestroy()
locationInfoCache.onDestroy()
relayListListener.onDestroy()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt
index 02c8bba71f..00fa67059c 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt
@@ -6,6 +6,8 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import java.text.DateFormat
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.ui.widget.Button
@@ -93,10 +95,14 @@ class AccountFragment : ServiceDependentFragment(OnNoService.GoBack) {
}
override fun onSafelyStart() {
- accountCache.onAccountNumberChange.subscribe(this) { accountNumber ->
- jobTracker.newUiJob("updateAccountNumber") {
- accountNumberView.information = accountNumber
- }
+ jobTracker.newUiJob("updateAccountNumber") {
+ deviceRepository.deviceState
+ .onEach { state ->
+ if (state.isInitialState()) deviceRepository.refreshDeviceState()
+ }
+ .collect {
+ accountNumberView.information = it.token()
+ }
}
accountCache.onAccountExpiryChange.subscribe(this) { accountExpiry ->
@@ -124,8 +130,8 @@ class AccountFragment : ServiceDependentFragment(OnNoService.GoBack) {
}
override fun onSafelyStop() {
- accountCache.onAccountNumberChange.unsubscribe(this)
accountCache.onAccountExpiryChange.unsubscribe(this)
+ jobTracker.cancelAllJobs()
}
override fun onSafelyDestroyView() {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt
index c6ce330128..5f0e417f58 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LaunchFragment.kt
@@ -4,18 +4,14 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.onSubscription
import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.model.DeviceState
+import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection
class LaunchFragment : ServiceAwareFragment() {
- private val hasAccountToken = CompletableDeferred<Boolean>()
-
- override fun onNewServiceConnection(serviceConnection: ServiceConnection) {
- serviceConnection.settingsListener.accountNumberNotifier.subscribe(this) { accountToken ->
- hasAccountToken.complete(accountToken != null)
- }
- }
override fun onCreateView(
inflater: LayoutInflater,
@@ -31,25 +27,27 @@ class LaunchFragment : ServiceAwareFragment() {
return view
}
- override fun onStart() {
- super.onStart()
-
- jobTracker.newUiJob("advanceToNextScreen") {
- advanceToNextScreen()
- }
- }
-
override fun onStop() {
jobTracker.cancelJob("advanceToNextScreen")
-
super.onStop()
}
- private suspend fun advanceToNextScreen() {
- if (hasAccountToken.await()) {
- advanceToConnectScreen()
- } else {
- advanceToLoginScreen()
+ override fun onNewServiceConnection(serviceConnection: ServiceConnection) {
+ advanceToNextScreen(serviceConnection.deviceRepository)
+ }
+
+ private fun advanceToNextScreen(deviceRepository: DeviceRepository) {
+ jobTracker.newUiJob("advanceToNextScreen") {
+ deviceRepository.deviceState
+ .onSubscription { deviceRepository.refreshDeviceState() }
+ .first { state -> state.isInitialState().not() }
+ .let { deviceState ->
+ when (deviceState) {
+ is DeviceState.DeviceRegistered -> advanceToConnectScreen()
+ is DeviceState.DeviceNotRegistered -> advanceToLoginScreen()
+ else -> Unit
+ }
+ }
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt
index c71c321b39..c2c1c2fc26 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt
@@ -10,6 +10,7 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoCache
import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache
import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy
import net.mullvad.mullvadvpn.ui.serviceconnection.CustomDns
+import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.KeyStatusListener
import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache
import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
@@ -48,6 +49,9 @@ abstract class ServiceDependentFragment(private val onNoService: OnNoService) :
lateinit var customDns: CustomDns
private set
+ lateinit var deviceRepository: DeviceRepository
+ private set
+
lateinit var keyStatusListener: KeyStatusListener
private set
@@ -70,6 +74,7 @@ abstract class ServiceDependentFragment(private val onNoService: OnNoService) :
appVersionInfoCache = serviceConnection.appVersionInfoCache
authTokenCache = serviceConnection.authTokenCache
connectionProxy = serviceConnection.connectionProxy
+ deviceRepository = serviceConnection.deviceRepository
customDns = serviceConnection.customDns
keyStatusListener = serviceConnection.keyStatusListener
locationInfoCache = serviceConnection.locationInfoCache
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
index 7204c1084c..9afd5bdb2c 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt
@@ -8,9 +8,12 @@ import android.widget.ImageButton
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onEach
import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.model.DeviceState
import net.mullvad.mullvadvpn.ui.serviceconnection.AccountCache
import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoCache
+import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnection
import net.mullvad.mullvadvpn.ui.widget.AccountCell
import net.mullvad.mullvadvpn.ui.widget.AppVersionCell
@@ -26,10 +29,12 @@ class SettingsFragment : ServiceAwareFragment(), StatusBarPainter, NavigationBar
private var active = false
private var accountCache: AccountCache? = null
+ private var deviceRepository: DeviceRepository? = null
private var versionInfoCache: AppVersionInfoCache? = null
override fun onNewServiceConnection(serviceConnection: ServiceConnection) {
accountCache = serviceConnection.accountCache
+ deviceRepository = serviceConnection.deviceRepository
versionInfoCache = serviceConnection.appVersionInfoCache
if (active) {
@@ -39,6 +44,7 @@ class SettingsFragment : ServiceAwareFragment(), StatusBarPainter, NavigationBar
override fun onNoServiceConnection() {
accountCache = null
+ deviceRepository = null
versionInfoCache = null
}
@@ -102,10 +108,11 @@ class SettingsFragment : ServiceAwareFragment(), StatusBarPainter, NavigationBar
versionInfoCache?.onUpdate = null
accountCache?.apply {
- onAccountNumberChange.unsubscribe(this@SettingsFragment)
onAccountExpiryChange.unsubscribe(this@SettingsFragment)
}
+ jobTracker.cancelAllJobs()
+
super.onStop()
}
@@ -116,12 +123,6 @@ class SettingsFragment : ServiceAwareFragment(), StatusBarPainter, NavigationBar
private fun configureListeners() {
accountCache?.apply {
- onAccountNumberChange.subscribe(this@SettingsFragment) { account ->
- jobTracker.newUiJob("updateLoggedInStatus") {
- updateLoggedInStatus(account != null)
- }
- }
-
onAccountExpiryChange.subscribe(this@SettingsFragment) { expiry ->
jobTracker.newUiJob("updateAccountInfo") {
accountMenu.accountExpiry = expiry
@@ -131,6 +132,16 @@ class SettingsFragment : ServiceAwareFragment(), StatusBarPainter, NavigationBar
fetchAccountExpiry()
}
+ jobTracker.newUiJob("updateLoggedInStatus") {
+ deviceRepository?.let { repository ->
+ repository.deviceState
+ .onEach { state -> if (state.isInitialState()) repository.refreshDeviceState() }
+ .collect { device ->
+ updateLoggedInStatus(device is DeviceState.DeviceRegistered)
+ }
+ }
+ }
+
versionInfoCache?.onUpdate = {
jobTracker.newUiJob("updateVersionInfo") {
updateVersionInfo()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt
index d9968a79a4..6bf4b5205e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt
@@ -57,9 +57,7 @@ class WelcomeFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
}
override fun onSafelyStart() {
- accountCache.onAccountNumberChange.subscribe(this) { account ->
- updateAccountNumber(account)
- }
+ updateAccountNumber(deviceRepository.deviceState.value.token())
accountCache.onAccountExpiryChange.subscribe(this) { expiry ->
checkExpiry(expiry)
@@ -76,7 +74,6 @@ class WelcomeFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) {
}
override fun onSafelyStop() {
- accountCache.onAccountNumberChange.unsubscribe(this)
accountCache.onAccountExpiryChange.unsubscribe(this)
jobTracker.cancelJob("pollAccountData")
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AccountCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AccountCache.kt
index 9bf5942f4c..6bec5c88b5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AccountCache.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AccountCache.kt
@@ -9,7 +9,6 @@ import net.mullvad.talpid.util.EventNotifier
import org.joda.time.DateTime
class AccountCache(private val connection: Messenger, eventDispatcher: EventDispatcher) {
- val onAccountNumberChange = EventNotifier<String?>(null)
val onAccountExpiryChange = EventNotifier<DateTime?>(null)
val onAccountHistoryChange = EventNotifier<String?>(null)
val onLoginStatusChange = EventNotifier<LoginStatus?>(null)
@@ -25,8 +24,6 @@ class AccountCache(private val connection: Messenger, eventDispatcher: EventDisp
registerHandler(Event.LoginStatus::class) { event ->
loginStatus = event.status
-
- onAccountNumberChange.notifyIfChanged(loginStatus?.account)
onAccountExpiryChange.notifyIfChanged(loginStatus?.expiry)
}
}
@@ -59,7 +56,6 @@ class AccountCache(private val connection: Messenger, eventDispatcher: EventDisp
}
fun onDestroy() {
- onAccountNumberChange.unsubscribeAll()
onAccountExpiryChange.unsubscribeAll()
onAccountHistoryChange.unsubscribeAll()
onLoginStatusChange.unsubscribeAll()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/DeviceRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/DeviceRepository.kt
new file mode 100644
index 0000000000..de796c5be3
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/DeviceRepository.kt
@@ -0,0 +1,20 @@
+package net.mullvad.mullvadvpn.ui.serviceconnection
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.stateIn
+import net.mullvad.mullvadvpn.model.DeviceState
+
+class DeviceRepository(
+ private val dataSource: ServiceConnectionDeviceDataSource,
+ externalScope: CoroutineScope
+) {
+ val deviceState = dataSource.deviceStateUpdates
+ .stateIn(
+ externalScope,
+ Lazily,
+ DeviceState.InitialState
+ )
+
+ fun refreshDeviceState() = dataSource.refreshDevice()
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt
index 861a54a561..bea6c34ecb 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnection.kt
@@ -4,6 +4,7 @@ import android.os.Looper
import android.os.Messenger
import android.os.RemoteException
import android.util.Log
+import kotlinx.coroutines.MainScope
import net.mullvad.mullvadvpn.di.SERVICE_CONNECTION_SCOPE
import net.mullvad.mullvadvpn.ipc.DispatchingHandler
import net.mullvad.mullvadvpn.ipc.Event
@@ -35,6 +36,8 @@ class ServiceConnection(
val accountCache = AccountCache(connection, dispatcher)
val authTokenCache = AuthTokenCache(connection, dispatcher)
val connectionProxy = ConnectionProxy(connection, dispatcher)
+ val deviceRepository =
+ DeviceRepository(ServiceConnectionDeviceDataSource(connection, dispatcher), MainScope())
val keyStatusListener = KeyStatusListener(connection, dispatcher)
val locationInfoCache = LocationInfoCache(dispatcher)
val settingsListener = SettingsListener(connection, dispatcher)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt
new file mode 100644
index 0000000000..60a2eb68e9
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt
@@ -0,0 +1,27 @@
+package net.mullvad.mullvadvpn.ui.serviceconnection
+
+import android.os.Messenger
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.callbackFlow
+import net.mullvad.mullvadvpn.ipc.Event
+import net.mullvad.mullvadvpn.ipc.EventDispatcher
+import net.mullvad.mullvadvpn.ipc.Request
+
+class ServiceConnectionDeviceDataSource(
+ private val connection: Messenger,
+ private val dispatcher: EventDispatcher
+) {
+ val deviceStateUpdates = callbackFlow {
+ dispatcher.registerHandler(Event.DeviceStateEvent::class) { event ->
+ trySend(event.newState)
+ }
+ awaitClose {
+ // The current dispatcher doesn't support unregistration of handlers.
+ }
+ }
+
+ // Async result: Event.DeviceChanged
+ fun refreshDevice() {
+ connection.send(Request.RefreshDeviceState.message)
+ }
+}