summaryrefslogtreecommitdiffhomepage
path: root/android/lib/ipc/src
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2023-07-26 13:24:34 +0200
committerAlbin <albin@mullvad.net>2023-07-27 10:41:29 +0200
commite65ec2a5a99cf1de9a99822b79c01ad90f0e2bc5 (patch)
tree2418f16fcbdcb2a4c1fd35babf608d0f3e19c6ae /android/lib/ipc/src
parent67d97bbf838d25151b1739960a74bb53d18c828c (diff)
downloadmullvadvpn-e65ec2a5a99cf1de9a99822b79c01ad90f0e2bc5.tar.xz
mullvadvpn-e65ec2a5a99cf1de9a99822b79c01ad90f0e2bc5.zip
Move ipc classes to ipc module
Diffstat (limited to 'android/lib/ipc/src')
-rw-r--r--android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/DispatchingHandler.kt41
-rw-r--r--android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Event.kt73
-rw-r--r--android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/HandlerFlow.kt41
-rw-r--r--android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Message.kt30
-rw-r--r--android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/MessageDispatcher.kt7
-rw-r--r--android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Request.kt103
6 files changed, 295 insertions, 0 deletions
diff --git a/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/DispatchingHandler.kt b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/DispatchingHandler.kt
new file mode 100644
index 0000000000..7f6195c61e
--- /dev/null
+++ b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/DispatchingHandler.kt
@@ -0,0 +1,41 @@
+package net.mullvad.mullvadvpn.lib.ipc
+
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.util.Log
+import java.util.concurrent.locks.ReentrantReadWriteLock
+import kotlin.concurrent.withLock
+import kotlin.reflect.KClass
+
+class DispatchingHandler<T : Any>(looper: Looper, private val extractor: (Message) -> T?) :
+ Handler(looper), MessageDispatcher<T> {
+ private val handlers = HashMap<KClass<out T>, (T) -> Unit>()
+ private val lock = ReentrantReadWriteLock()
+
+ override fun <V : T> registerHandler(variant: KClass<V>, handler: (V) -> Unit) {
+ lock.writeLock().withLock {
+ handlers.put(variant) { instance -> @Suppress("UNCHECKED_CAST") handler(instance as V) }
+ }
+ }
+
+ override fun handleMessage(message: Message) {
+ lock.readLock().withLock {
+ val instance = extractor(message)
+
+ if (instance != null) {
+ val handler = handlers.get(instance::class)
+
+ handler?.invoke(instance)
+ } else {
+ Log.e("mullvad", "Dispatching handler received an unexpected message")
+ }
+ }
+ }
+
+ fun onDestroy() {
+ lock.writeLock().withLock { handlers.clear() }
+
+ removeCallbacksAndMessages(null)
+ }
+}
diff --git a/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Event.kt b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Event.kt
new file mode 100644
index 0000000000..9ce1ccc698
--- /dev/null
+++ b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Event.kt
@@ -0,0 +1,73 @@
+package net.mullvad.mullvadvpn.lib.ipc
+
+import android.os.Messenger
+import kotlinx.parcelize.Parcelize
+import net.mullvad.mullvadvpn.model.AccountCreationResult
+import net.mullvad.mullvadvpn.model.AccountExpiry
+import net.mullvad.mullvadvpn.model.AccountHistory
+import net.mullvad.mullvadvpn.model.AppVersionInfo
+import net.mullvad.mullvadvpn.model.DeviceListEvent
+import net.mullvad.mullvadvpn.model.DeviceState
+import net.mullvad.mullvadvpn.model.GeoIpLocation
+import net.mullvad.mullvadvpn.model.LoginResult
+import net.mullvad.mullvadvpn.model.RelayList
+import net.mullvad.mullvadvpn.model.RemoveDeviceResult
+import net.mullvad.mullvadvpn.model.Settings
+import net.mullvad.mullvadvpn.model.TunnelState
+import net.mullvad.mullvadvpn.model.VoucherSubmissionResult
+
+// Events that can be sent from the service
+sealed class Event : Message.EventMessage() {
+ protected override val messageKey = MESSAGE_KEY
+
+ @Parcelize data class AccountCreationEvent(val result: AccountCreationResult) : Event()
+
+ @Parcelize data class AccountExpiryEvent(val expiry: AccountExpiry) : Event()
+
+ @Parcelize data class AccountHistoryEvent(val history: AccountHistory) : Event()
+
+ @Parcelize
+ data class AppVersionInfo(val versionInfo: net.mullvad.mullvadvpn.model.AppVersionInfo?) :
+ Event()
+
+ @Parcelize data class AuthToken(val token: String?) : Event()
+
+ @Parcelize data class CurrentVersion(val version: String?) : Event()
+
+ @Parcelize data class DeviceStateEvent(val newState: DeviceState) : Event()
+
+ @Parcelize data class DeviceListUpdate(val event: DeviceListEvent) : Event()
+
+ @Parcelize
+ data class DeviceRemovalEvent(val deviceId: String, val result: RemoveDeviceResult) : Event()
+
+ @Parcelize data class ListenerReady(val connection: Messenger, val listenerId: Int) : Event()
+
+ @Parcelize data class LoginEvent(val result: LoginResult) : Event()
+
+ @Parcelize data class NewLocation(val location: GeoIpLocation?) : Event()
+
+ @Parcelize data class NewRelayList(val relayList: RelayList?) : Event()
+
+ @Parcelize data class SettingsUpdate(val settings: Settings?) : Event()
+
+ @Parcelize data class SplitTunnelingUpdate(val excludedApps: List<String>?) : Event()
+
+ @Parcelize data class TunnelStateChange(val tunnelState: TunnelState) : Event()
+
+ @Parcelize
+ data class VoucherSubmissionResult(
+ val voucher: String,
+ val result: net.mullvad.mullvadvpn.model.VoucherSubmissionResult
+ ) : Event()
+
+ @Parcelize object VpnPermissionRequest : Event()
+
+ companion object {
+ private const val MESSAGE_KEY = "event"
+
+ fun fromMessage(message: android.os.Message): Event? = fromMessage(message, MESSAGE_KEY)
+ }
+}
+
+typealias EventDispatcher = MessageDispatcher<Event>
diff --git a/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/HandlerFlow.kt b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/HandlerFlow.kt
new file mode 100644
index 0000000000..4d60cf4d7b
--- /dev/null
+++ b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/HandlerFlow.kt
@@ -0,0 +1,41 @@
+package net.mullvad.mullvadvpn.lib.ipc
+
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.util.Log
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.ClosedSendChannelException
+import kotlinx.coroutines.channels.trySendBlocking
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.consumeAsFlow
+import kotlinx.coroutines.flow.onCompletion
+
+class HandlerFlow<T>(looper: Looper, private val extractor: (Message) -> T) :
+ Handler(looper), Flow<T> {
+ private val channel = Channel<T>(Channel.UNLIMITED)
+ private val flow = channel.consumeAsFlow().onCompletion { removeCallbacksAndMessages(null) }
+
+ @InternalCoroutinesApi
+ override suspend fun collect(collector: FlowCollector<T>) = flow.collect(collector)
+
+ override fun handleMessage(message: Message) {
+ val extractedData = extractor(message)
+
+ try {
+ channel.trySendBlocking(extractedData)
+ } catch (exception: Exception) {
+ when (exception) {
+ is ClosedSendChannelException,
+ is CancellationException -> {
+ Log.w("mullvad", "Received a message after HandlerFlow was closed", exception)
+ removeCallbacksAndMessages(null)
+ }
+ else -> throw exception
+ }
+ }
+ }
+}
diff --git a/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Message.kt b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Message.kt
new file mode 100644
index 0000000000..dd5f9442e1
--- /dev/null
+++ b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Message.kt
@@ -0,0 +1,30 @@
+package net.mullvad.mullvadvpn.lib.ipc
+
+import android.os.Bundle
+import android.os.Message as RawMessage
+import android.os.Parcelable
+
+sealed class Message(private val messageId: Int) : Parcelable {
+ abstract class EventMessage : Message(1)
+ abstract class RequestMessage : Message(2)
+
+ protected abstract val messageKey: String
+
+ val message: RawMessage
+ get() =
+ RawMessage.obtain().also { message ->
+ message.what = messageId
+ message.data = Bundle()
+ message.data.putParcelable(messageKey, this)
+ }
+
+ companion object {
+ internal fun <T : Parcelable> fromMessage(message: RawMessage, key: String): T? {
+ val data = message.data
+
+ data.classLoader = Message::class.java.classLoader
+
+ return data.getParcelable(key)
+ }
+ }
+}
diff --git a/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/MessageDispatcher.kt b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/MessageDispatcher.kt
new file mode 100644
index 0000000000..8bb6703479
--- /dev/null
+++ b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/MessageDispatcher.kt
@@ -0,0 +1,7 @@
+package net.mullvad.mullvadvpn.lib.ipc
+
+import kotlin.reflect.KClass
+
+interface MessageDispatcher<T : Any> {
+ fun <V : T> registerHandler(variant: KClass<V>, handler: (V) -> Unit)
+}
diff --git a/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Request.kt b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Request.kt
new file mode 100644
index 0000000000..ad91632cbf
--- /dev/null
+++ b/android/lib/ipc/src/main/kotlin/net/mullvad/mullvadvpn/lib/ipc/Request.kt
@@ -0,0 +1,103 @@
+package net.mullvad.mullvadvpn.lib.ipc
+
+import android.os.Message as RawMessage
+import android.os.Messenger
+import java.net.InetAddress
+import kotlinx.parcelize.Parcelize
+import net.mullvad.mullvadvpn.model.DnsOptions
+import net.mullvad.mullvadvpn.model.GeographicLocationConstraint
+import net.mullvad.mullvadvpn.model.ObfuscationSettings
+import net.mullvad.mullvadvpn.model.QuantumResistantState
+import net.mullvad.mullvadvpn.model.WireguardConstraints
+
+// Requests that the service can handle
+sealed class Request : Message.RequestMessage() {
+ protected override val messageKey = MESSAGE_KEY
+
+ @Parcelize
+ @Deprecated("Use SetDnsOptions")
+ data class AddCustomDnsServer(val address: InetAddress) : Request()
+
+ @Parcelize object Connect : Request()
+
+ @Parcelize object CreateAccount : Request()
+
+ @Parcelize object Disconnect : Request()
+
+ @Parcelize data class ExcludeApp(val packageName: String) : Request()
+
+ @Parcelize object FetchAccountExpiry : Request()
+
+ @Parcelize object FetchAccountHistory : Request()
+
+ @Parcelize object FetchAuthToken : Request()
+
+ @Parcelize data class IncludeApp(val packageName: String) : Request()
+
+ @Parcelize data class Login(val account: String?) : Request()
+
+ @Parcelize object RefreshDeviceState : Request()
+
+ @Parcelize object GetDevice : Request()
+
+ @Parcelize data class GetDeviceList(val accountToken: String) : Request()
+
+ @Parcelize data class RemoveDevice(val accountToken: String, val deviceId: String) : Request()
+
+ @Parcelize object Logout : Request()
+
+ @Parcelize object PersistExcludedApps : Request()
+
+ @Parcelize object Reconnect : Request()
+
+ @Parcelize data class RegisterListener(val listener: Messenger) : Request()
+
+ @Parcelize object ClearAccountHistory : Request()
+
+ @Parcelize
+ @Deprecated("Use SetDnsOptions")
+ data class RemoveCustomDnsServer(val address: InetAddress) : Request()
+
+ @Parcelize
+ @Deprecated("Use SetDnsOptions")
+ data class ReplaceCustomDnsServer(val oldAddress: InetAddress, val newAddress: InetAddress) :
+ Request()
+
+ @Parcelize data class SetAllowLan(val allow: Boolean) : Request()
+
+ @Parcelize data class SetAutoConnect(val autoConnect: Boolean) : Request()
+
+ @Parcelize
+ @Deprecated("Use SetDnsOptions")
+ data class SetEnableCustomDns(val enable: Boolean) : Request()
+
+ @Parcelize data class SetEnableSplitTunneling(val enable: Boolean) : Request()
+
+ @Parcelize
+ data class SetRelayLocation(val relayLocation: GeographicLocationConstraint?) : Request()
+
+ @Parcelize data class SetWireGuardMtu(val mtu: Int?) : Request()
+
+ @Parcelize data class SubmitVoucher(val voucher: String) : Request()
+
+ @Parcelize data class UnregisterListener(val listenerId: Int) : Request()
+
+ @Parcelize data class VpnPermissionResponse(val isGranted: Boolean) : Request()
+
+ @Parcelize data class SetDnsOptions(val dnsOptions: DnsOptions) : Request()
+
+ @Parcelize data class SetObfuscationSettings(val settings: ObfuscationSettings?) : Request()
+
+ @Parcelize
+ data class SetWireguardConstraints(val wireguardConstraints: WireguardConstraints?) : Request()
+
+ @Parcelize
+ data class SetWireGuardQuantumResistant(val quantumResistant: QuantumResistantState) :
+ Request()
+
+ companion object {
+ private const val MESSAGE_KEY = "request"
+
+ fun fromMessage(message: RawMessage): Request? = fromMessage(message, MESSAGE_KEY)
+ }
+}