diff options
Diffstat (limited to 'android/src')
17 files changed, 129 insertions, 21 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 28f180ac04..8fea50d719 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt @@ -21,6 +21,9 @@ sealed class Event : Message.EventMessage() { data class AppVersionInfo(val versionInfo: AppVersionInfoData?) : Event() @Parcelize + data class AuthToken(val token: String?) : Event() + + @Parcelize data class CurrentVersion(val version: String?) : Event() @Parcelize 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 fc293e69a3..db72cb08c5 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt @@ -29,6 +29,9 @@ sealed class Request : Message.RequestMessage() { object FetchAccountExpiry : Request() @Parcelize + object FetchAuthToken : Request() + + @Parcelize data class IncludeApp(val packageName: String) : Request() @Parcelize diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt new file mode 100644 index 0000000000..1ea2acec39 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt @@ -0,0 +1,49 @@ +package net.mullvad.mullvadvpn.service.endpoint + +import kotlin.properties.Delegates.observable +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 + +class AuthTokenCache(endpoint: ServiceEndpoint) { + companion object { + private enum class Command { + Fetch + } + } + + private val daemon = endpoint.intermittentDaemon + private val requestQueue = spawnActor() + + var authToken by observable<String?>(null) { _, _, token -> + endpoint.sendEvent(Event.AuthToken(token)) + } + private set + + init { + endpoint.dispatcher.registerHandler(Request.FetchAuthToken::class) { _ -> + requestQueue.sendBlocking(Command.Fetch) + } + } + + fun onDestroy() { + requestQueue.close() + } + + private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + for (command in channel) { + when (command) { + Command.Fetch -> authToken = daemon.await().getWwwAuthToken() + } + } + } catch (exception: ClosedReceiveChannelException) { + // Closed sender, so stop the actor + } + } +} 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 da0926034b..f251f39340 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 @@ -41,6 +41,7 @@ class ServiceEndpoint( val accountCache = AccountCache(this) val appVersionInfoCache = AppVersionInfoCache(this) + val authTokenCache = AuthTokenCache(this) val customDns = CustomDns(this) val keyStatusListener = KeyStatusListener(this) val locationInfoCache = LocationInfoCache(this) @@ -59,6 +60,7 @@ class ServiceEndpoint( accountCache.onDestroy() appVersionInfoCache.onDestroy() + authTokenCache.onDestroy() connectionProxy.onDestroy() customDns.onDestroy() keyStatusListener.onDestroy() @@ -116,6 +118,7 @@ class ServiceEndpoint( Event.CurrentVersion(appVersionInfoCache.currentVersion), Event.AppVersionInfo(appVersionInfoCache.appVersionInfo), Event.NewRelayList(relayListListener.relayList), + Event.AuthToken(authTokenCache.authToken), Event.ListenerReady ) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt index 846bf996e6..2b8d9341cf 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/AccountFragment.kt @@ -68,7 +68,7 @@ class AccountFragment : ServiceDependentFragment(OnNoService.GoBack) { sitePaymentButton = view.findViewById<SitePaymentButton>(R.id.site_payment).apply { newAccount = false - prepare(daemon, jobTracker) { + prepare(authTokenCache, jobTracker) { checkForAddedTime() } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt index 9c8e797945..6f701c9ba7 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt @@ -52,9 +52,9 @@ class ConnectFragment : notificationBanner = view.findViewById<NotificationBanner>(R.id.notification_banner).apply { notifications.apply { register(TunnelStateNotification(parentActivity, connectionProxy)) - register(KeyStatusNotification(parentActivity, daemon, keyStatusListener)) + register(KeyStatusNotification(parentActivity, authTokenCache, keyStatusListener)) register(VersionInfoNotification(parentActivity, appVersionInfoCache)) - register(AccountExpiryNotification(parentActivity, daemon, accountCache)) + register(AccountExpiryNotification(parentActivity, authTokenCache, accountCache)) } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/OutOfTimeFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/OutOfTimeFragment.kt index 8443d7c530..1de849529e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/OutOfTimeFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/OutOfTimeFragment.kt @@ -53,7 +53,7 @@ class OutOfTimeFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) sitePaymentButton = view.findViewById<SitePaymentButton>(R.id.site_payment).apply { newAccount = false - prepare(daemon, jobTracker) + prepare(authTokenCache, jobTracker) } redeemButton = view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { 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 0da72332a8..c7358f7a88 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ServiceDependentFragment.kt @@ -8,6 +8,7 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.service.MullvadDaemon import net.mullvad.mullvadvpn.ui.serviceconnection.AccountCache 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.KeyStatusListener @@ -40,6 +41,9 @@ abstract class ServiceDependentFragment(private val onNoService: OnNoService) : lateinit var appVersionInfoCache: AppVersionInfoCache private set + lateinit var authTokenCache: AuthTokenCache + private set + lateinit var connectionProxy: ConnectionProxy private set @@ -69,6 +73,7 @@ abstract class ServiceDependentFragment(private val onNoService: OnNoService) : // initialization of the fields doesn't have to be synchronized accountCache = serviceConnection.accountCache appVersionInfoCache = serviceConnection.appVersionInfoCache + authTokenCache = serviceConnection.authTokenCache connectionProxy = serviceConnection.connectionProxy customDns = serviceConnection.customDns daemon = serviceConnection.daemon diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt index 8d4500c6f2..9aadce014b 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WelcomeFragment.kt @@ -43,7 +43,7 @@ class WelcomeFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) { view.findViewById<SitePaymentButton>(R.id.site_payment).apply { newAccount = true - prepare(daemon, jobTracker) + prepare(authTokenCache, jobTracker) } view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WireguardKeyFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WireguardKeyFragment.kt index b5494cd82a..4e5601c2c3 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WireguardKeyFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/WireguardKeyFragment.kt @@ -138,7 +138,7 @@ class WireguardKeyFragment : ServiceDependentFragment(OnNoService.GoToLaunchScre verifyingKeySpinner = view.findViewById(R.id.verifying_key_spinner) manageKeysButton = view.findViewById<UrlButton>(R.id.manage_keys).apply { - prepare(daemon, jobTracker) + prepare(authTokenCache, jobTracker) } titleController = CollapsibleTitleController(view) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/AccountExpiryNotification.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/AccountExpiryNotification.kt index b45a4e53e8..b959a06607 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/AccountExpiryNotification.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/AccountExpiryNotification.kt @@ -2,16 +2,16 @@ package net.mullvad.mullvadvpn.ui.notification import android.content.Context import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.service.MullvadDaemon import net.mullvad.mullvadvpn.ui.serviceconnection.AccountCache +import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache import net.mullvad.mullvadvpn.util.TimeLeftFormatter import org.joda.time.DateTime class AccountExpiryNotification( context: Context, - daemon: MullvadDaemon, + authTokenCache: AuthTokenCache, private val accountCache: AccountCache -) : NotificationWithUrlWithToken(context, daemon, R.string.account_url) { +) : NotificationWithUrlWithToken(context, authTokenCache, R.string.account_url) { private val timeLeftFormatter = TimeLeftFormatter(context.resources) init { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/KeyStatusNotification.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/KeyStatusNotification.kt index fba57eca03..93bd4f4cb3 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/KeyStatusNotification.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/KeyStatusNotification.kt @@ -3,14 +3,14 @@ package net.mullvad.mullvadvpn.ui.notification import android.content.Context import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.KeygenEvent -import net.mullvad.mullvadvpn.service.MullvadDaemon +import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache import net.mullvad.mullvadvpn.ui.serviceconnection.KeyStatusListener class KeyStatusNotification( context: Context, - daemon: MullvadDaemon, + authTokenCache: AuthTokenCache, private val keyStatusListener: KeyStatusListener -) : NotificationWithUrlWithToken(context, daemon, R.string.wg_key_url) { +) : NotificationWithUrlWithToken(context, authTokenCache, R.string.wg_key_url) { private val failedToGenerateKey = context.getString(R.string.failed_to_generate_key) private val tooManyKeys = context.getString(R.string.too_many_keys) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrlWithToken.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrlWithToken.kt index 2c8c713a83..6afd54840c 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrlWithToken.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrlWithToken.kt @@ -3,11 +3,11 @@ package net.mullvad.mullvadvpn.ui.notification import android.content.Context import android.content.Intent import android.net.Uri -import net.mullvad.mullvadvpn.service.MullvadDaemon +import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache abstract class NotificationWithUrlWithToken( protected val context: Context, - protected val daemon: MullvadDaemon, + protected val authTokenCache: AuthTokenCache, urlId: Int ) : InAppNotification() { private val url = context.getString(urlId) @@ -21,5 +21,5 @@ abstract class NotificationWithUrlWithToken( showIcon = true } - private fun buildUrl() = Uri.parse("$url?token=${daemon.getWwwAuthToken()}") + private suspend fun buildUrl() = Uri.parse("$url?token=${authTokenCache.fetchAuthToken()}") } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt new file mode 100644 index 0000000000..29077e55f6 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt @@ -0,0 +1,42 @@ +package net.mullvad.mullvadvpn.ui.serviceconnection + +import android.os.Messenger +import java.util.LinkedList +import kotlinx.coroutines.CompletableDeferred +import net.mullvad.mullvadvpn.ipc.DispatchingHandler +import net.mullvad.mullvadvpn.ipc.Event +import net.mullvad.mullvadvpn.ipc.Request + +class AuthTokenCache(val connection: Messenger, eventDispatcher: DispatchingHandler<Event>) { + private val fetchQueue = LinkedList<CompletableDeferred<String>>() + + init { + eventDispatcher.registerHandler(Event.AuthToken::class) { event -> + synchronized(this@AuthTokenCache) { + fetchQueue.poll()?.complete(event.token ?: "") + } + } + } + + suspend fun fetchAuthToken(): String { + val authToken = CompletableDeferred<String>() + + synchronized(this) { + fetchQueue.offer(authToken) + } + + connection.send(Request.FetchAuthToken.message) + + return authToken.await() + } + + fun onDestroy() { + synchronized(this) { + for (pendingFetch in fetchQueue) { + pendingFetch.cancel() + } + + fetchQueue.clear() + } + } +} 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 61f2f6ef5e..95c28b810e 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 @@ -32,6 +32,7 @@ class ServiceConnection(private val service: ServiceInstance) : KoinScopeCompone val daemon = service.daemon val accountCache = AccountCache(service.messenger, dispatcher) + val authTokenCache = AuthTokenCache(service.messenger, dispatcher) val connectionProxy = ConnectionProxy(service.messenger, dispatcher) val keyStatusListener = KeyStatusListener(service.messenger, dispatcher) val locationInfoCache = LocationInfoCache(dispatcher) @@ -54,6 +55,7 @@ class ServiceConnection(private val service: ServiceInstance) : KoinScopeCompone dispatcher.onDestroy() accountCache.onDestroy() + authTokenCache.onDestroy() connectionProxy.onDestroy() keyStatusListener.onDestroy() locationInfoCache.onDestroy() diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt index 6a20bcbd7f..46f738258e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt @@ -110,6 +110,7 @@ class NotificationBanner : FrameLayout { fun onDestroy() { notifications.onDestroy() + jobTracker.cancelAllJobs() } protected override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt index ddef67c634..613f65524e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt @@ -8,11 +8,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.service.MullvadDaemon +import net.mullvad.mullvadvpn.ui.serviceconnection.AuthTokenCache import net.mullvad.mullvadvpn.util.JobTracker open class UrlButton : Button { - private lateinit var daemon: MullvadDaemon + private lateinit var authTokenCache: AuthTokenCache private var shouldEnable = true @@ -46,7 +46,7 @@ open class UrlButton : Button { } fun prepare( - daemon: MullvadDaemon, + authTokenCache: AuthTokenCache, jobTracker: JobTracker, jobName: String = "fetchUrl", extraOnClickAction: (suspend () -> Unit)? = null @@ -54,7 +54,7 @@ open class UrlButton : Button { synchronized(this) { super.setEnabled(shouldEnable) - this.daemon = daemon + this.authTokenCache = authTokenCache setOnClickAction(jobName, jobTracker) { super.setEnabled(false) @@ -71,7 +71,7 @@ open class UrlButton : Button { synchronized(this) { shouldEnable = enabled - if (!withToken || this::daemon.isInitialized) { + if (!withToken || this::authTokenCache.isInitialized) { super.setEnabled(enabled) } } @@ -98,7 +98,7 @@ open class UrlButton : Button { private suspend fun buildIntent(jobTracker: JobTracker): Intent { val buildIntent = GlobalScope.async(Dispatchers.Default) { val uri = if (withToken) { - Uri.parse(url + "?token=" + daemon.getWwwAuthToken()) + Uri.parse(url + "?token=" + authTokenCache.fetchAuthToken()) } else { Uri.parse(url) } |
