Fix application hangs in disconnection state

This commit is contained in:
albexk 2023-12-07 22:43:33 +03:00
parent 2eaaf01ca1
commit 8cc5846808

View file

@ -12,6 +12,7 @@ import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.os.Process
import androidx.annotation.MainThread
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
@ -21,12 +22,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.coroutines.withTimeout
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.LoadLibraryException
import org.amnezia.vpn.protocol.Protocol
@ -57,7 +60,8 @@ const val AFTER_PERMISSION_CHECK = "AFTER_PERMISSION_CHECK"
private const val PREFS_CONFIG_KEY = "LAST_CONF"
private const val NOTIFICATION_ID = 1337
private const val STATISTICS_SENDING_TIMEOUT = 1000L
private const val DISCONNECT_TIMEOUT = 1500L
private const val DISCONNECT_TIMEOUT = 5000L
private const val STOP_SERVICE_TIMEOUT = 5000L
class AmneziaVpnService : VpnService() {
@ -214,7 +218,7 @@ class AmneziaVpnService : VpnService() {
isServiceBound = false
stopSendingStatistics()
clientMessenger.reset()
if (isUnknown || isDisconnected) stopSelf()
if (isUnknown || isDisconnected) stopService()
}
return true
}
@ -239,16 +243,25 @@ class AmneziaVpnService : VpnService() {
override fun onDestroy() {
Log.v(TAG, "Destroy service")
runBlocking {
withTimeoutOrNull(DISCONNECT_TIMEOUT) {
disconnect()
disconnectionJob?.join()
}
disconnect()
disconnectionJob?.join()
}
connectionScope.cancel()
mainScope.cancel()
super.onDestroy()
}
private fun stopService() {
Log.v(TAG, "Stop service")
// the coroutine below will be canceled during the onDestroy call
mainScope.launch {
delay(STOP_SERVICE_TIMEOUT)
Log.w(TAG, "Stop service timeout, kill process")
Process.killProcess(Process.myPid())
}
stopSelf()
}
/**
* Methods responsible for processing VPN connection
*/
@ -265,7 +278,7 @@ class AmneziaVpnService : VpnService() {
DISCONNECTED -> {
clientMessenger.send(ServiceEvent.DISCONNECTED)
stopSendingStatistics()
if (!isServiceBound) stopSelf()
if (!isServiceBound) stopService()
}
DISCONNECTING -> {
@ -301,10 +314,10 @@ class AmneziaVpnService : VpnService() {
@MainThread
private fun connect(vpnConfig: String?) {
Log.v(TAG, "Start VPN connection")
if (isConnected || protocolState.value == CONNECTING) return
Log.v(TAG, "Start VPN connection")
protocolState.value = CONNECTING
val config = parseConfigToJson(vpnConfig)
@ -330,10 +343,10 @@ class AmneziaVpnService : VpnService() {
@MainThread
private fun disconnect() {
Log.v(TAG, "Stop VPN connection")
if (isUnknown || isDisconnected || protocolState.value == DISCONNECTING) return
Log.v(TAG, "Stop VPN connection")
protocolState.value = DISCONNECTING
disconnectionJob = connectionScope.launch {
@ -342,6 +355,15 @@ class AmneziaVpnService : VpnService() {
protocol?.stopVpn()
protocol = null
try {
withTimeout(DISCONNECT_TIMEOUT) {
// waiting for disconnect state
protocolState.first { it == DISCONNECTED }
}
} catch (e: TimeoutCancellationException) {
Log.w(TAG, "Disconnect timeout")
stopService()
}
}
}