Commit 7d52441c authored by Daniel Wolf's avatar Daniel Wolf
Browse files

Show a notification when the VPN seems to have disconnected

parent ae14ccff
...@@ -95,6 +95,7 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope { ...@@ -95,6 +95,7 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope {
private var localResolver:LocalResolver? = null private var localResolver:LocalResolver? = null
private var runInNonVpnMode:Boolean = getPreferences().runWithoutVpn private var runInNonVpnMode:Boolean = getPreferences().runWithoutVpn
private var connectionWatchDog:ConnectionWatchdog? = null private var connectionWatchDog:ConnectionWatchdog? = null
private var vpnWatchdog:VpnWatchDog? = null
private var watchdogDisabledForSession = false private var watchdogDisabledForSession = false
private val coroutineSupervisor = SupervisorJob() private val coroutineSupervisor = SupervisorJob()
@Suppress("EXPERIMENTAL_API_USAGE") @Suppress("EXPERIMENTAL_API_USAGE")
...@@ -912,6 +913,7 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope { ...@@ -912,6 +913,7 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope {
vpnProxy?.stop() vpnProxy?.stop()
dnsServerProxy?.stop() dnsServerProxy?.stop()
connectionWatchDog?.stop() connectionWatchDog?.stop()
vpnWatchdog?.stop()
if(ipTablesRedirector == null || ipTablesRedirector?.endForward() == IpTablesPacketRedirector.IpTablesMode.SUCCEEDED) { if(ipTablesRedirector == null || ipTablesRedirector?.endForward() == IpTablesPacketRedirector.IpTablesMode.SUCCEEDED) {
getPreferences().lastIptablesRedirectAddress = null getPreferences().lastIptablesRedirectAddress = null
getPreferences().lastIptablesRedirectAddressIPv6 = null getPreferences().lastIptablesRedirectAddressIPv6 = null
...@@ -973,16 +975,20 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope { ...@@ -973,16 +975,20 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope {
BackgroundVpnConfigureActivity.prepareVpn(this, userServerConfig) BackgroundVpnConfigureActivity.prepareVpn(this, userServerConfig)
}, 250) }, 250)
} else if(getPreferences().showNotificationOnRevoked){ } else if(getPreferences().showNotificationOnRevoked){
NotificationCompat.Builder(this, Notifications.getHighPriorityChannelId(this)).apply { showVpnRevokedNotification()
setSmallIcon(R.drawable.ic_cloud_warn) }
setContentTitle(getString(R.string.notification_service_revoked_title)) }
setContentText(getString(R.string.notification_service_revoked_message))
setContentIntent(PendingIntent.getActivity(this@DnsVpnService, RequestCodes.RESTART_AFTER_REVOKE, Intent(this@DnsVpnService, BackgroundVpnConfigureActivity::class.java), PendingIntent.FLAG_CANCEL_CURRENT)) private fun showVpnRevokedNotification() {
setAutoCancel(true) NotificationCompat.Builder(this, Notifications.getHighPriorityChannelId(this)).apply {
priority = NotificationCompat.PRIORITY_HIGH setSmallIcon(R.drawable.ic_cloud_warn)
}.build().also { setContentTitle(getString(R.string.notification_service_revoked_title))
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(Notifications.ID_SERVICE_REVOKED, it) setContentText(getString(R.string.notification_service_revoked_message))
} setContentIntent(PendingIntent.getActivity(this@DnsVpnService, RequestCodes.RESTART_AFTER_REVOKE, Intent(this@DnsVpnService, BackgroundVpnConfigureActivity::class.java), PendingIntent.FLAG_CANCEL_CURRENT))
setAutoCancel(true)
priority = NotificationCompat.PRIORITY_HIGH
}.build().also {
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(Notifications.ID_SERVICE_REVOKED, it)
} }
} }
...@@ -1383,7 +1389,6 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope { ...@@ -1383,7 +1389,6 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope {
val usedAddress = if(bindAddress == defaultBindAddress) "localhost" else bindAddress.hostAddress val usedAddress = if(bindAddress == defaultBindAddress) "localhost" else bindAddress.hostAddress
showDnsServerModeNotification(usedAddress, actualPort, preferredPort, iptablesMode) showDnsServerModeNotification(usedAddress, actualPort, preferredPort, iptablesMode)
currentTrafficStats = vpnProxy?.trafficStats currentTrafficStats = vpnProxy?.trafficStats
createConnectionWatchdog()
log("Non-VPN proxy started.") log("Non-VPN proxy started.")
} }
} else { } else {
...@@ -1427,6 +1432,9 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope { ...@@ -1427,6 +1432,9 @@ class DnsVpnService : VpnService(), Runnable, CoroutineScope {
hideBadConnectionNotification() hideBadConnectionNotification()
}, logger = this@DnsVpnService.logger, advancedLogging = getPreferences().advancedLogging) }, logger = this@DnsVpnService.logger, advancedLogging = getPreferences().advancedLogging)
} }
vpnWatchdog = VpnWatchDog({
showVpnRevokedNotification()
}, this)
} }
private fun createQueryLogger(): QueryListener? { private fun createQueryLogger(): QueryListener? {
......
package com.frostnerd.smokescreen.service
import android.content.Context
import android.net.ConnectivityManager
import com.frostnerd.smokescreen.Logger
import com.frostnerd.smokescreen.isVpnNetwork
import com.frostnerd.smokescreen.logger
import kotlinx.coroutines.*
/*
* Copyright (C) 2020 Daniel Wolf (Ch4t4r)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can contact the developer at daniel.wolf@frostnerd.com.
*/
class VpnWatchDog(private val onVpnDisconnected:() -> Unit,
private val context: Context) {
private val supervisor = SupervisorJob()
private val scope = CoroutineScope(supervisor + Dispatchers.IO)
private var running = true
private var vpnRunning:Boolean = true
private val logger:Logger? = context.logger
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
init {
scope.launch {
checkConnection()
}
}
private fun log(text:String) {
logger?.log(text, "ConnectionWatchdog")
}
private suspend fun checkConnection() {
delay(3000)
log("Beginning VPN-check")
val networks = connectivityManager.allNetworks.toList().filterNotNull()
val isStillRunning = networks.any { connectivityManager.isVpnNetwork(it) } || networks.isEmpty()
if(!isStillRunning && vpnRunning) {
callCallback()
}
vpnRunning = isStillRunning
if(running) {
log("Connection check done.")
scope.launch {
checkConnection()
}
}
}
private fun callCallback() {
if(!running) return
log("Calling callback.")
onVpnDisconnected()
}
fun stop() {
supervisor.cancel()
running = false
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment