diff options
| author | David Göransson <david.goransson@mullvad.net> | 2024-05-29 17:18:29 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2024-05-29 17:18:29 +0200 |
| commit | ad90145a5d86d8c1e6e70f2238f11edf5e50f8d8 (patch) | |
| tree | 9d085bc81caed9409e3a4360490c06c2da4fbba8 /android/lib/common/src | |
| parent | 8e14a8d4287af66a57a98db79d3ac320c2dad4a1 (diff) | |
| parent | 767b97eda756f4ec4e67fb5fa2ae664277291e8f (diff) | |
| download | mullvadvpn-ad90145a5d86d8c1e6e70f2238f11edf5e50f8d8.tar.xz mullvadvpn-ad90145a5d86d8c1e6e70f2238f11edf5e50f8d8.zip | |
Merge branch 'android-grpc'
Diffstat (limited to 'android/lib/common/src')
9 files changed, 29 insertions, 281 deletions
diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/ClassesAndActions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/ClassNames.kt index 09210ffa03..1636bbd46f 100644 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/ClassesAndActions.kt +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/ClassNames.kt @@ -2,13 +2,8 @@ package net.mullvad.mullvadvpn.lib.common.constant // Do not use in cases where the application id is expected since the application id will differ // between different builds. -private const val MULLVAD_PACKAGE_NAME = "net.mullvad.mullvadvpn" +internal const val MULLVAD_PACKAGE_NAME = "net.mullvad.mullvadvpn" // Classes const val MAIN_ACTIVITY_CLASS = "$MULLVAD_PACKAGE_NAME.ui.MainActivity" const val VPN_SERVICE_CLASS = "$MULLVAD_PACKAGE_NAME.service.MullvadVpnService" - -// Actions -const val KEY_CONNECT_ACTION = "$MULLVAD_PACKAGE_NAME.connect_action" -const val KEY_DISCONNECT_ACTION = "$MULLVAD_PACKAGE_NAME.disconnect_action" -const val KEY_QUIT_ACTION = "$MULLVAD_PACKAGE_NAME.quit_action" diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt new file mode 100644 index 0000000000..ea420f2d0a --- /dev/null +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/IntentActions.kt @@ -0,0 +1,6 @@ +package net.mullvad.mullvadvpn.lib.common.constant + +// Actions +const val KEY_CONNECT_ACTION = "$MULLVAD_PACKAGE_NAME.connect_action" +const val KEY_DISCONNECT_ACTION = "$MULLVAD_PACKAGE_NAME.disconnect_action" +const val KEY_REQUEST_VPN_PERMISSION = "$MULLVAD_PACKAGE_NAME.request_vpn_permission" diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/LogTag.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/LogTag.kt new file mode 100644 index 0000000000..d2ae3f1871 --- /dev/null +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/constant/LogTag.kt @@ -0,0 +1,3 @@ +package net.mullvad.mullvadvpn.lib.common.constant + +const val TAG = "mullvad" diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt index 42f0663967..bf94c80778 100644 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/CommonFlowUtils.kt @@ -1,47 +1,9 @@ package net.mullvad.mullvadvpn.lib.common.util -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.os.IBinder -import android.util.Log -import kotlin.coroutines.EmptyCoroutineContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import net.mullvad.mullvadvpn.model.ServiceResult +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.withTimeoutOrNull -fun <T> SendChannel<T>.safeOffer(element: T): Boolean { - return runCatching { trySend(element).isSuccess }.getOrDefault(false) -} - -fun Context.bindServiceFlow(intent: Intent, flags: Int = 0): Flow<ServiceResult> = callbackFlow { - val connectionCallback = - object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, binder: IBinder) { - safeOffer(ServiceResult(binder)) - } - - override fun onServiceDisconnected(className: ComponentName) { - safeOffer(ServiceResult.NOT_CONNECTED) - bindService(intent, this, flags) - } - } - - bindService(intent, connectionCallback, flags) - - awaitClose { - safeOffer(ServiceResult.NOT_CONNECTED) - - Dispatchers.Default.dispatch(EmptyCoroutineContext) { - try { - unbindService(connectionCallback) - } catch (e: IllegalArgumentException) { - Log.e("mullvad", "Cannot unbind as no binding exists.") - } - } - } +suspend fun <T> Flow<T>.firstOrNullWithTimeout(timeMillis: Long): T? { + return withTimeoutOrNull(timeMillis) { firstOrNull() } } diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt index 8ef70dad92..d714dae327 100644 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ContextExtensions.kt @@ -4,15 +4,20 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.Settings -import net.mullvad.mullvadvpn.lib.common.R import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.getInstalledPackagesList +import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken private const val ALWAYS_ON_VPN_APP = "always_on_vpn_app" -fun Context.openAccountPageInBrowser(authToken: String) { - startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.account_url) + "?token=$authToken")) - ) +fun createAccountUri(accountUri: String, websiteAuthToken: WebsiteAuthToken?): Uri { + val urlString = buildString { + append(accountUri) + if (websiteAuthToken != null) { + append("?token=") + append(websiteAuthToken.value) + } + } + return Uri.parse(urlString) } fun Context.getAlwaysOnVpnAppName(): String? { diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DispatchingFlow.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DispatchingFlow.kt deleted file mode 100644 index 7fc37a752c..0000000000 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/DispatchingFlow.kt +++ /dev/null @@ -1,45 +0,0 @@ -package net.mullvad.mullvadvpn.lib.common.util - -import java.util.concurrent.ConcurrentHashMap -import kotlin.reflect.KClass -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.channels.ClosedSendChannelException -import kotlinx.coroutines.channels.SendChannel -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.consumeAsFlow - -class DispatchingFlow<T : Any>(private val upstream: Flow<T>) : Flow<T> { - private val subscribers = ConcurrentHashMap<KClass<out T>, SendChannel<T>>() - - fun <V : T> subscribe(variant: KClass<V>, capacity: Int = Channel.CONFLATED): Flow<V> { - val channel = Channel<V>(capacity) - - // This is safe because `collect` will only send to this channel if the instance class is V - @Suppress("UNCHECKED_CAST") - subscribers[variant] = channel as SendChannel<T> - - return channel.consumeAsFlow() - } - - fun <V : T> unsubscribe(variant: KClass<V>) = subscribers.remove(variant) - - @InternalCoroutinesApi - override suspend fun collect(collector: FlowCollector<T>) { - upstream.collect { event -> - try { - subscribers[event::class]?.send(event) - } catch (closedException: ClosedSendChannelException) { - subscribers.remove(event::class) - } - - collector.emit(event) - } - - subscribers.clear() - } -} - -fun <T : Any> Flow<T>.dispatchTo(configureSubscribers: DispatchingFlow<T>.() -> Unit) = - DispatchingFlow(this).also(configureSubscribers) diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt index f906ee8f6d..2c9554a842 100644 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt +++ b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/ErrorStateExtension.kt @@ -2,9 +2,9 @@ package net.mullvad.mullvadvpn.lib.common.util import android.content.Context import net.mullvad.mullvadvpn.lib.common.R -import net.mullvad.talpid.tunnel.ErrorState -import net.mullvad.talpid.tunnel.ErrorStateCause -import net.mullvad.talpid.tunnel.ParameterGenerationError +import net.mullvad.mullvadvpn.lib.model.ErrorState +import net.mullvad.mullvadvpn.lib.model.ErrorStateCause +import net.mullvad.mullvadvpn.lib.model.ParameterGenerationError import net.mullvad.talpid.util.addressString fun ErrorState.getErrorNotificationResources(context: Context): ErrorNotificationMessage { @@ -48,8 +48,8 @@ fun ErrorStateCause.errorMessageId(): Int { is ErrorStateCause.InvalidDnsServers -> R.string.invalid_dns_servers is ErrorStateCause.AuthFailed -> R.string.auth_failed is ErrorStateCause.Ipv6Unavailable -> R.string.ipv6_unavailable - is ErrorStateCause.SetFirewallPolicyError -> R.string.set_firewall_policy_error - is ErrorStateCause.SetDnsError -> R.string.set_dns_error + is ErrorStateCause.FirewallPolicyError -> R.string.set_firewall_policy_error + is ErrorStateCause.DnsError -> R.string.set_dns_error is ErrorStateCause.StartTunnelError -> R.string.start_tunnel_error is ErrorStateCause.IsOffline -> R.string.is_offline is ErrorStateCause.TunnelParameterError -> { diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/Intermittent.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/Intermittent.kt deleted file mode 100644 index 448d96778f..0000000000 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/Intermittent.kt +++ /dev/null @@ -1,87 +0,0 @@ -package net.mullvad.mullvadvpn.lib.common.util - -import kotlin.properties.Delegates.observable -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.sync.withPermit -import net.mullvad.talpid.util.EventNotifier - -// Wrapper to allow awaiting for intermittent values. -// -// Wraps a property that is changed from time to time and that can become unavailable (null). This -// behaves in a way similar to `CompletableDeferred`, but the value can be set and reset multiple -// times. -// -// Calling `await` will either provide the value if it's available, or suspend until it becomes -// available and then return it. -// -// Calling `update` will set the internal value after it guarantees that no other coroutine is -// currently reading the value (through a permit from the semaphore). After the value is set, it -// provides a permit to the semaphore so that suspended coroutines can use the new value. -// -// Extra initialization can be done on the intermittent value when it becomes available and before -// it is provided to the awaiting coroutines, through the use of listener callbacks. These are -// called after the value is updated but before it is made available to the coroutines. -class Intermittent<T> { - private val notifier = EventNotifier<T?>(null) - private val semaphore = Semaphore(1, 1) - private val writeLock = Mutex() - - private var updateJob: Job? = null - private var value by notifier.notifiable() - - // When the internal value is updated, listeners can be notified before the awaiting coroutines - // resume execution. This allows performing any extra initialization before the value is made - // available for usage. - fun registerListener(id: Any, listener: (T?) -> Unit) = notifier.subscribe(id, listener) - - fun unregisterListener(id: Any) = notifier.unsubscribe(id) - - suspend fun await(): T { - return semaphore.withPermit { value!! } - } - - suspend fun update(newValue: T?) { - writeLock.withLock { - if (newValue != value) { - if (value != null) { - semaphore.acquire() - } - - // This will trigger the listeners to run before the awaiting coroutines resume - value = newValue - - if (newValue != null) { - semaphore.release() - } - } - } - } - - // Helper method that spawns a coroutine to update the value. - fun spawnUpdate(newValue: T?) { - synchronized(this@Intermittent) { - val previousUpdate = updateJob - - updateJob = - GlobalScope.launch(Dispatchers.Default) { - previousUpdate?.join() - update(newValue) - } - } - } - - // Helper method that provides a simple way to change the wrapped value. - // The method returns a property delegate that will spawn a coroutine to update the wrapped - // value every time the property is written to. - fun source() = observable<T?>(null) { _, _, newValue -> spawnUpdate(newValue) } - - fun onDestroy() { - notifier.unsubscribeAll() - } -} diff --git a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/JobTracker.kt b/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/JobTracker.kt deleted file mode 100644 index edb76ed4ae..0000000000 --- a/android/lib/common/src/main/kotlin/net/mullvad/mullvadvpn/lib/common/util/JobTracker.kt +++ /dev/null @@ -1,91 +0,0 @@ -package net.mullvad.mullvadvpn.lib.common.util - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.async -import kotlinx.coroutines.launch - -class JobTracker { - private val jobs = HashMap<Long, Job>() - private val reaperJobs = HashMap<Long, Job>() - private val namedJobs = HashMap<String, Long>() - - private var jobIdCounter = 0L - - fun newJob(job: Job): Long { - synchronized(jobs) { - val jobId = jobIdCounter - - jobIdCounter += 1 - - jobs.put(jobId, job) - - reaperJobs.put( - jobId, - GlobalScope.launch(Dispatchers.Default) { - job.join() - - synchronized(jobs) { jobs.remove(jobId) } - } - ) - - return jobId - } - } - - fun newJob(name: String, job: Job): Long { - synchronized(namedJobs) { - cancelJob(name) - - val newJobId = newJob(job) - - namedJobs.put(name, newJobId) - - return newJobId - } - } - - fun newBackgroundJob(name: String, jobBody: suspend () -> Unit): Long { - return newJob(name, GlobalScope.launch(Dispatchers.Default) { jobBody() }) - } - - fun newUiJob(name: String, jobBody: suspend () -> Unit): Long { - return newJob(name, GlobalScope.launch(Dispatchers.Main) { jobBody() }) - } - - suspend fun <T> runOnBackground(jobBody: suspend () -> T): T { - val job = GlobalScope.async(Dispatchers.Default) { jobBody() } - - newJob(job) - - return job.await() - } - - fun cancelJob(name: String) { - synchronized(namedJobs) { namedJobs.remove(name)?.let { oldJobId -> cancelJob(oldJobId) } } - } - - fun cancelJob(jobId: Long) { - synchronized(jobs) { - jobs.remove(jobId)?.cancel() - reaperJobs.remove(jobId)?.cancel() - } - } - - fun cancelAllJobs() { - synchronized(jobs) { - for (job in jobs.values) { - job.cancel() - } - - for (job in reaperJobs.values) { - job.cancel() - } - - jobs.clear() - reaperJobs.clear() - namedJobs.clear() - } - } -} |
