summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2022-12-13 10:37:16 +0100
committerAlbin <albin@mullvad.net>2023-01-10 15:32:33 +0100
commit3cf1466817025198bc3138774a724d49bb857914 (patch)
tree38018dca3ea02ac5af85c946edde976430d5c5e1 /android
parentca8878aa238a5c8e3be1f326ce98e238ffe87388 (diff)
downloadmullvadvpn-3cf1466817025198bc3138774a724d49bb857914.tar.xz
mullvadvpn-3cf1466817025198bc3138774a724d49bb857914.zip
Add option to use custom api endpoint in debug builds
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt56
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt12
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt33
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt13
-rw-r--r--android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointIntentExtensions.kt18
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt8
-rw-r--r--android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt4
-rw-r--r--android/test/e2e/build.gradle.kts2
-rw-r--r--android/test/mockapi/build.gradle.kts1
10 files changed, 111 insertions, 44 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt
index 0d1750f625..67ec721a52 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt
@@ -2,6 +2,8 @@ package net.mullvad.mullvadvpn.service
import java.io.File
import kotlin.properties.Delegates.observable
+import kotlin.reflect.KClass
+import kotlin.reflect.safeCast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
@@ -9,14 +11,17 @@ import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.channels.trySendBlocking
+import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointConfiguration
import net.mullvad.mullvadvpn.util.Intermittent
private const val RELAYS_FILE = "relays.json"
-class DaemonInstance(val vpnService: MullvadVpnService) {
- private enum class Command {
- START,
- STOP,
+class DaemonInstance(
+ val vpnService: MullvadVpnService
+) {
+ sealed class Command {
+ data class Start(val apiEndpointConfiguration: ApiEndpointConfiguration) : Command()
+ object Stop : Command()
}
private val commandChannel = spawnActor()
@@ -27,12 +32,12 @@ class DaemonInstance(val vpnService: MullvadVpnService) {
val intermittentDaemon = Intermittent<MullvadDaemon>()
- fun start() {
- commandChannel.trySendBlocking(Command.START)
+ fun start(apiEndpointConfiguration: ApiEndpointConfiguration) {
+ commandChannel.trySendBlocking(Command.Start(apiEndpointConfiguration))
}
fun stop() {
- commandChannel.trySendBlocking(Command.STOP)
+ commandChannel.trySendBlocking(Command.Stop)
}
fun onDestroy() {
@@ -46,30 +51,25 @@ class DaemonInstance(val vpnService: MullvadVpnService) {
prepareFiles()
while (isRunning) {
- if (!waitForCommand(channel, Command.START)) {
- break
- }
-
- startDaemon()
-
- isRunning = waitForCommand(channel, Command.STOP)
-
+ val startCommand = waitForCommand(channel, Command.Start::class) ?: break
+ startDaemon(startCommand.apiEndpointConfiguration)
+ isRunning = waitForCommand(channel, Command.Stop::class) is Command.Stop
stopDaemon()
}
}
- private suspend fun waitForCommand(
+ private suspend fun <T : Command> waitForCommand(
channel: ReceiveChannel<Command>,
- command: Command
- ): Boolean {
- try {
- while (channel.receive() != command) {
- // Wait for command
- }
-
- return true
+ command: KClass<T>
+ ): T? {
+ return try {
+ var receivedCommand: T?
+ do {
+ receivedCommand = command.safeCast(channel.receive())
+ } while (receivedCommand == null)
+ receivedCommand
} catch (exception: ClosedReceiveChannelException) {
- return false
+ null
}
}
@@ -91,8 +91,10 @@ class DaemonInstance(val vpnService: MullvadVpnService) {
}
}
- private suspend fun startDaemon() {
- val newDaemon = MullvadDaemon(vpnService).apply {
+ private suspend fun startDaemon(
+ apiEndpointConfiguration: ApiEndpointConfiguration
+ ) {
+ val newDaemon = MullvadDaemon(vpnService, apiEndpointConfiguration).apply {
onDaemonStopped = {
intermittentDaemon.spawnUpdate(null)
daemon = null
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 cd92a27472..6d82ee617c 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
@@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.service
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpoint
+import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointConfiguration
import net.mullvad.mullvadvpn.model.AppVersionInfo
import net.mullvad.mullvadvpn.model.Device
import net.mullvad.mullvadvpn.model.DeviceEvent
@@ -21,7 +22,10 @@ import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.model.VoucherSubmissionResult
import net.mullvad.talpid.util.EventNotifier
-class MullvadDaemon(vpnService: MullvadVpnService) {
+class MullvadDaemon(
+ vpnService: MullvadVpnService,
+ apiEndpointConfiguration: ApiEndpointConfiguration
+) {
protected var daemonInterfaceAddress = 0L
val onSettingsChange = EventNotifier<Settings?>(null)
@@ -39,8 +43,12 @@ class MullvadDaemon(vpnService: MullvadVpnService) {
init {
System.loadLibrary("mullvad_jni")
+
initialize(
- vpnService, vpnService.cacheDir.absolutePath, vpnService.filesDir.absolutePath, null
+ vpnService = vpnService,
+ cacheDirectory = vpnService.cacheDir.absolutePath,
+ resourceDirectory = vpnService.filesDir.absolutePath,
+ apiEndpoint = apiEndpointConfiguration.apiEndpoint()
)
onSettingsChange.notify(getSettings())
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
index 4753294376..c35125c326 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
@@ -12,7 +12,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.BuildConfig
import net.mullvad.mullvadvpn.di.vpnServiceModule
+import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointConfiguration
+import net.mullvad.mullvadvpn.lib.endpoint.DefaultApiEndpointConfiguration
+import net.mullvad.mullvadvpn.lib.endpoint.getApiEndpointConfigurationExtras
import net.mullvad.mullvadvpn.model.Settings
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.service.endpoint.ServiceEndpoint
@@ -64,11 +68,15 @@ class MullvadVpnService : TalpidVpnService() {
}
}
+ private var apiEndpointConfiguration: ApiEndpointConfiguration =
+ DefaultApiEndpointConfiguration()
+
override fun onCreate() {
super.onCreate()
Log.d(TAG, "Initializing service")
loadKoinModules(vpnServiceModule)
+
daemonInstance = DaemonInstance(this)
keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
@@ -97,14 +105,6 @@ class MullvadVpnService : TalpidVpnService() {
endpoint.accountCache
)
- daemonInstance.apply {
- intermittentDaemon.registerListener(this@MullvadVpnService) { daemon ->
- handleDaemonInstance(daemon)
- }
-
- start()
- }
-
// Remove any leftover tunnel state persistence data
getSharedPreferences("tunnel_state", MODE_PRIVATE)
.edit()
@@ -114,6 +114,21 @@ class MullvadVpnService : TalpidVpnService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "Starting service")
+
+ if (BuildConfig.DEBUG) {
+ intent?.getApiEndpointConfigurationExtras()?.let {
+ apiEndpointConfiguration = it
+ }
+ }
+
+ daemonInstance.apply {
+ intermittentDaemon.registerListener(this@MullvadVpnService) { daemon ->
+ handleDaemonInstance(daemon)
+ }
+
+ start(apiEndpointConfiguration)
+ }
+
val startResult = super.onStartCommand(intent, flags, startId)
var quitCommand = false
@@ -231,7 +246,7 @@ class MullvadVpnService : TalpidVpnService() {
daemonInstance.apply {
stop()
- start()
+ start(apiEndpointConfiguration)
}
} else {
Log.d(TAG, "Ignoring restart because onDestroy has executed")
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
index becf82a69e..fc53d94aaf 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
@@ -22,7 +22,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
@@ -34,6 +33,7 @@ import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.component.ChangelogDialog
import net.mullvad.mullvadvpn.dataproxy.MullvadProblemReport
import net.mullvad.mullvadvpn.di.uiModule
+import net.mullvad.mullvadvpn.lib.endpoint.getApiEndpointConfigurationExtras
import net.mullvad.mullvadvpn.model.AccountExpiry
import net.mullvad.mullvadvpn.model.DeviceState
import net.mullvad.mullvadvpn.ui.fragments.DeviceRevokedFragment
@@ -103,7 +103,11 @@ open class MainActivity : FragmentActivity() {
override fun onStart() {
Log.d("mullvad", "Starting main activity")
super.onStart()
- serviceConnectionManager.bind(vpnPermissionRequestHandler = ::requestVpnPermission)
+
+ serviceConnectionManager.bind(
+ vpnPermissionRequestHandler = ::requestVpnPermission,
+ apiEndpointConfiguration = intent?.getApiEndpointConfigurationExtras()
+ )
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
index e1654476b8..f912f765a8 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
@@ -8,6 +8,9 @@ import android.os.Messenger
import android.util.Log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointConfiguration
+import net.mullvad.mullvadvpn.lib.endpoint.BuildConfig
+import net.mullvad.mullvadvpn.lib.endpoint.putApiEndpointConfigurationExtra
import net.mullvad.mullvadvpn.service.MullvadVpnService
import net.mullvad.talpid.util.EventNotifier
@@ -47,9 +50,17 @@ class ServiceConnectionManager(
}
}
- fun bind(vpnPermissionRequestHandler: () -> Unit) {
+ fun bind(
+ vpnPermissionRequestHandler: () -> Unit,
+ apiEndpointConfiguration: ApiEndpointConfiguration?
+ ) {
this.vpnPermissionRequestHandler = vpnPermissionRequestHandler
val intent = Intent(context, MullvadVpnService::class.java)
+
+ if (BuildConfig.DEBUG && apiEndpointConfiguration != null) {
+ intent.putApiEndpointConfigurationExtra(apiEndpointConfiguration)
+ }
+
context.startService(intent)
context.bindService(intent, serviceConnection, 0)
}
diff --git a/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointIntentExtensions.kt b/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointIntentExtensions.kt
new file mode 100644
index 0000000000..cf2f2fb0dd
--- /dev/null
+++ b/android/lib/endpoint/src/main/kotlin/net/mullvad/mullvadvpn/lib/endpoint/ApiEndpointIntentExtensions.kt
@@ -0,0 +1,18 @@
+package net.mullvad.mullvadvpn.lib.endpoint
+
+import android.content.Intent
+import android.os.Build
+
+private const val OVERRIDE_API_EXTRA_NAME = "override_api"
+
+fun Intent.putApiEndpointConfigurationExtra(apiEndpointConfiguration: ApiEndpointConfiguration) {
+ putExtra(OVERRIDE_API_EXTRA_NAME, apiEndpointConfiguration)
+}
+
+fun Intent.getApiEndpointConfigurationExtras(): ApiEndpointConfiguration? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ getParcelableExtra(OVERRIDE_API_EXTRA_NAME, ApiEndpointConfiguration::class.java)
+ } else {
+ getParcelableExtra(OVERRIDE_API_EXTRA_NAME)
+ }
+}
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
index e225997534..83d217236f 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt
@@ -6,6 +6,8 @@ import android.widget.ImageButton
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
+import net.mullvad.mullvadvpn.lib.endpoint.CustomApiEndpointConfiguration
+import net.mullvad.mullvadvpn.lib.endpoint.putApiEndpointConfigurationExtra
import net.mullvad.mullvadvpn.test.common.constant.APP_LAUNCH_TIMEOUT
import net.mullvad.mullvadvpn.test.common.constant.CONNECTION_TIMEOUT
import net.mullvad.mullvadvpn.test.common.constant.LOGIN_TIMEOUT
@@ -19,17 +21,21 @@ class AppInteractor(
private val device: UiDevice,
private val targetContext: Context
) {
- fun launch() {
+ fun launch(customApiEndpointConfiguration: CustomApiEndpointConfiguration? = null) {
device.pressHome()
// Wait for launcher
device.wait(
Until.hasObject(By.pkg(device.launcherPackageName).depth(0)),
APP_LAUNCH_TIMEOUT
)
+
val intent =
targetContext.packageManager.getLaunchIntentForPackage(MULLVAD_PACKAGE)?.apply {
// Clear out any previous instances
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ if (customApiEndpointConfiguration != null) {
+ putApiEndpointConfigurationExtra(customApiEndpointConfiguration)
+ }
}
targetContext.startActivity(intent)
device.wait(
diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt
index 7f70d98ca7..73d515c501 100644
--- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt
+++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt
@@ -4,10 +4,10 @@ import android.util.Log
import androidx.test.runner.screenshot.BasicScreenCaptureProcessor
import androidx.test.runner.screenshot.ScreenCaptureProcessor
import androidx.test.runner.screenshot.Screenshot
-import org.junit.rules.TestWatcher
-import org.junit.runner.Description
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
class CaptureScreenshotOnFailedTestRule(private val logTag: String) : TestWatcher() {
override fun failed(e: Throwable?, description: Description?) {
diff --git a/android/test/e2e/build.gradle.kts b/android/test/e2e/build.gradle.kts
index 8aaf83742c..a8f0714782 100644
--- a/android/test/e2e/build.gradle.kts
+++ b/android/test/e2e/build.gradle.kts
@@ -106,6 +106,8 @@ configure<org.owasp.dependencycheck.gradle.extension.DependencyCheckExtension> {
dependencies {
implementation(project(Projects.testCommon))
+ implementation(project(Dependencies.Mullvad.endpointLib))
+
implementation(Dependencies.AndroidX.testCore)
// Fixes: https://github.com/android/android-test/issues/1589
implementation(Dependencies.AndroidX.testMonitor)
diff --git a/android/test/mockapi/build.gradle.kts b/android/test/mockapi/build.gradle.kts
index 84410e168f..d464e2dc5a 100644
--- a/android/test/mockapi/build.gradle.kts
+++ b/android/test/mockapi/build.gradle.kts
@@ -47,6 +47,7 @@ configure<org.owasp.dependencycheck.gradle.extension.DependencyCheckExtension> {
dependencies {
implementation(project(Projects.testCommon))
+ implementation(project(Dependencies.Mullvad.endpointLib))
implementation(Dependencies.AndroidX.testCore)
// Fixes: https://github.com/android/android-test/issues/1589