diff --git a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt index abe46245e..dd096c606 100644 --- a/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt +++ b/client/android/openvpn/src/main/kotlin/org/amnezia/vpn/protocol/openvpn/OpenVpn.kt @@ -58,7 +58,7 @@ open class OpenVpn : Protocol() { scope = CoroutineScope(Dispatchers.IO) } - override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { + override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { val configBuilder = OpenVpnConfig.Builder() openVpnClient = OpenVpnClient( diff --git a/client/android/protocolApi/src/main/kotlin/Protocol.kt b/client/android/protocolApi/src/main/kotlin/Protocol.kt index a475a2fcb..24cbc595b 100644 --- a/client/android/protocolApi/src/main/kotlin/Protocol.kt +++ b/client/android/protocolApi/src/main/kotlin/Protocol.kt @@ -42,7 +42,7 @@ abstract class Protocol { protected abstract fun internalInit() - abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) + abstract suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) abstract fun stopVpn() diff --git a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt index b30f15032..54330861b 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaVpnService.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel +import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.drop @@ -111,6 +112,10 @@ open class AmneziaVpnService : VpnService() { get() = clientMessengers.any { it.value.name == ACTIVITY_MESSENGER_NAME } private val connectionExceptionHandler = CoroutineExceptionHandler { _, e -> + connectionJob?.cancel() + connectionJob = null + disconnectionJob?.cancel() + disconnectionJob = null protocolState.value = DISCONNECTED when (e) { is IllegalArgumentException, @@ -531,7 +536,7 @@ open class AmneziaVpnService : VpnService() { protocolState.value = DISCONNECTING disconnectionJob = connectionScope.launch { - connectionJob?.join() + connectionJob?.cancelAndJoin() connectionJob = null vpnProto?.protocol?.stopVpn() diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index 690510eb7..b5804a6dc 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -1,7 +1,12 @@ package org.amnezia.vpn.protocol.wireguard import android.net.VpnService.Builder +import java.io.IOException +import java.util.Locale import java.util.TreeMap +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext import org.amnezia.awg.GoBackend import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.ProtocolState.CONNECTED @@ -79,12 +84,44 @@ open class Wireguard : Protocol() { if (!isInitialized) loadSharedLibrary(context, "wg-go") } - override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { + override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { val wireguardConfig = parseConfig(config) + val startTime = System.currentTimeMillis() start(wireguardConfig, vpnBuilder, protect) + waitForConnection(startTime) state.value = CONNECTED } + private suspend fun waitForConnection(startTime: Long) { + Log.d(TAG, "Waiting for connection") + withContext(Dispatchers.IO) { + val time = String.format(Locale.ROOT,"%.3f", startTime / 1000.0) + try { + delay(1000) + var log = getLogcat(time) + Log.d(TAG, "First waiting log: $log") + // check that there is a connection log, + // to avoid infinite connection + if (!log.contains("Attaching to interface")) { + Log.w(TAG, "Logs do not contain a connection log") + return@withContext + } + while (!log.contains("Received handshake response")) { + delay(1000) + log = getLogcat(time) + } + } catch (e: IOException) { + Log.e(TAG, "Failed to get logcat: $e") + } + } + } + + private fun getLogcat(time: String): String = + ProcessBuilder("logcat", "--buffer=main", "--format=raw", "*:S AmneziaWG/awg0", "-t", time) + .redirectErrorStream(true) + .start() + .inputStream.reader().readText() + protected open fun parseConfig(config: JSONObject): WireguardConfig { val configDataJson = config.getJSONObject("wireguard_config_data") val configData = parseConfigData(configDataJson.getString("config")) diff --git a/client/android/xray/src/main/kotlin/Xray.kt b/client/android/xray/src/main/kotlin/Xray.kt index 3e5f9f7c9..94afec2ac 100644 --- a/client/android/xray/src/main/kotlin/Xray.kt +++ b/client/android/xray/src/main/kotlin/Xray.kt @@ -109,7 +109,7 @@ class Xray : Protocol() { } } - override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { + override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { if (isRunning) { Log.w(TAG, "XRay already running") return