fix: tile auto tunnel toggle and wifi internet connection check

Change tile toggle behavior to also toggle auto tunnel service to allow user temporary override and re-enablement of auto tunnel from the tile.

Add tunnel name to tile.

Fix bug where wifi networks without internet access were impacting auto tunneling determinations.

Closes #22
This commit is contained in:
Zane Schepke 2023-09-01 13:05:17 -04:00
parent 0e64bbb4e1
commit eeccc71469
9 changed files with 77 additions and 48 deletions

View File

@ -17,7 +17,7 @@ android {
val versionMajor = 2
val versionMinor = 3
val versionPatch = 6
val versionPatch = 7
val versionBuild = 0
defaultConfig {

View File

@ -7,6 +7,7 @@ import com.wireguard.android.backend.Tunnel
import com.zaneschepke.wireguardautotunnel.R
import com.zaneschepke.wireguardautotunnel.repository.Repository
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceState
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceTracker
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardConnectivityWatcherService
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
@ -39,18 +40,15 @@ class TunnelControlTile : TileService() {
private lateinit var job : Job
override fun onStartListening() {
if (!this::job.isInitialized) {
job = scope.launch {
updateTileState()
}
job = scope.launch {
updateTileState()
}
Timber.d("On start listening")
super.onStartListening()
}
override fun onTileAdded() {
super.onTileAdded()
qsTile.contentDescription = "Toggle VPN"
qsTile.contentDescription = this.resources.getString(R.string.toggle_vpn)
scope.launch {
updateTileState();
}
@ -62,43 +60,50 @@ class TunnelControlTile : TileService() {
}
override fun onClick() {
super.onClick()
unlockAndRun {
scope.launch {
try {
if(vpnService.getState() == Tunnel.State.UP) {
stopTunnel();
return@launch
}
val settings = settingsRepo.getAll()
if (!settings.isNullOrEmpty()) {
val setting = settings.first()
if (setting.defaultTunnel != null) {
startTunnel(setting.defaultTunnel!!)
val tunnel = determineTileTunnel();
if(tunnel != null) {
attemptWatcherServiceToggle(tunnel.toString())
if(vpnService.getState() == Tunnel.State.UP) {
stopTunnelService();
} else {
val config = configRepo.getAll()?.first();
if(config != null) {
startTunnel(config.toString());
}
startTunnelService(tunnel.toString())
}
}
} catch (e : Exception) {
Timber.e(e.message)
} finally {
cancel()
}
}
super.onClick()
}
}
private fun stopTunnel() {
private suspend fun determineTileTunnel() : TunnelConfig? {
var tunnelConfig : TunnelConfig? = null;
val settings = settingsRepo.getAll()
if (!settings.isNullOrEmpty()) {
val setting = settings.first()
tunnelConfig = if (setting.defaultTunnel != null) {
TunnelConfig.from(setting.defaultTunnel!!);
} else {
val config = configRepo.getAll()?.first();
config;
}
}
return tunnelConfig;
}
private fun stopTunnelService() {
ServiceTracker.actionOnService(
Action.STOP, this@TunnelControlTile,
WireGuardConnectivityWatcherService::class.java)
ServiceTracker.actionOnService(
Action.STOP, this@TunnelControlTile,
Action.STOP, this.applicationContext,
WireGuardTunnelService::class.java)
}
private fun startTunnel(tunnelConfig : String) {
private fun startTunnelService(tunnelConfig : String) {
ServiceTracker.actionOnService(
Action.START, this.applicationContext,
WireGuardTunnelService::class.java,
@ -107,42 +112,64 @@ class TunnelControlTile : TileService() {
tunnelConfig))
}
private fun startWatcherService(tunnelConfig : String) {
ServiceTracker.actionOnService(
Action.START, this,
WireGuardConnectivityWatcherService::class.java, mapOf(this.resources.
getString(R.string.tunnel_extras_key) to
tunnelConfig))
}
private fun stopWatcherService() {
ServiceTracker.actionOnService(
Action.STOP, this,
WireGuardConnectivityWatcherService::class.java)
}
private fun attemptWatcherServiceToggle(tunnelConfig : String) {
scope.launch {
val settings = settingsRepo.getAll()
if (!settings.isNullOrEmpty()) {
val setting = settings.first()
if(setting.isAutoTunnelEnabled) {
when(ServiceTracker.getServiceState( this@TunnelControlTile,
WireGuardConnectivityWatcherService::class.java,)) {
ServiceState.STARTED -> stopWatcherService()
ServiceState.STOPPED -> startWatcherService(tunnelConfig)
}
}
}
}
}
private suspend fun updateTileState() {
vpnService.state.collect {
when(it) {
Tunnel.State.UP -> {
setTileOn()
qsTile.state = Tile.STATE_ACTIVE
}
Tunnel.State.DOWN -> {
setTileOff()
qsTile.state = Tile.STATE_INACTIVE;
}
else -> {
qsTile.state = Tile.STATE_UNAVAILABLE
}
}
val config = determineTileTunnel();
setTileDescription(config?.name ?: this.resources.getString(R.string.no_tunnel_available))
qsTile.updateTile()
}
}
private fun setTileOff() {
qsTile.state = Tile.STATE_INACTIVE;
private fun setTileDescription(description : String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
qsTile.subtitle = "Off"
qsTile.subtitle = description
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
qsTile.stateDescription = "VPN Off";
qsTile.stateDescription = description;
}
}
private fun setTileOn() {
qsTile.state = Tile.STATE_ACTIVE;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
qsTile.subtitle = "On"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
qsTile.stateDescription = "VPN On";
}
}
private fun cancelJob() {
if(this::job.isInitialized) {
job.cancel();

View File

@ -4,10 +4,7 @@ import android.app.Service
import android.content.Intent
import android.os.Bundle
import android.os.IBinder
import com.zaneschepke.wireguardautotunnel.repository.Repository
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
import timber.log.Timber
import javax.inject.Inject
open class ForegroundService : Service() {

View File

@ -24,6 +24,7 @@ object ServiceTracker {
fun <T : Service> actionOnService(action: Action, application: Application, cls : Class<T>, extras : Map<String,String>? = null) {
if (getServiceState(application, cls) == ServiceState.STOPPED && action == Action.STOP) return
if (getServiceState(application, cls) == ServiceState.STARTED && action == Action.START) return
val intent = Intent(application, cls).also {
it.action = action.name
extras?.forEach {(k, v) ->
@ -40,6 +41,7 @@ object ServiceTracker {
fun <T : Service> actionOnService(action: Action, context: Context, cls : Class<T>, extras : Map<String,String>? = null) {
if (getServiceState(context, cls) == ServiceState.STOPPED && action == Action.STOP) return
if (getServiceState(context, cls) == ServiceState.STARTED && action == Action.START) return
val intent = Intent(context, cls).also {
it.action = action.name
extras?.forEach {(k, v) ->

View File

@ -85,7 +85,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
}
}
cancelWatcherJob()
stopVPN()
stopSelf()
}

View File

@ -28,7 +28,6 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
object : ConnectivityManager.NetworkCallback(
FLAG_INCLUDE_LOCATION_INFO
) {
override fun onAvailable(network: Network) {
trySend(NetworkStatus.Available(network))
}
@ -68,6 +67,8 @@ abstract class BaseNetworkService<T : BaseNetworkService<T>>(val context: Contex
}
val request = NetworkRequest.Builder()
.addTransportType(networkCapability)
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
.build()
connectivityManager.registerNetworkCallback(request, networkStatusCallback)

View File

@ -30,6 +30,7 @@ class WireGuardTunnel @Inject constructor(private val backend : Backend,
override val tunnelName get() = _tunnelName.asStateFlow()
private val _state = MutableSharedFlow<Tunnel.State>(
onBufferOverflow = BufferOverflow.DROP_OLDEST,
replay = 1)
private val _handshakeStatus = MutableSharedFlow<HandshakeStatus>(replay = 1,

View File

@ -11,5 +11,5 @@ data class Settings(
var isTunnelOnMobileDataEnabled : Boolean = false,
var trustedNetworkSSIDs : MutableList<String> = mutableListOf(),
var defaultTunnel : String? = null,
var isAlwaysOnVpnEnabled : Boolean = false
var isAlwaysOnVpnEnabled : Boolean = false,
)

View File

@ -84,4 +84,6 @@
<string name="detecting_location_services_disabled">Detecting Location Services disabled</string>
<string name="precise_location_message">This feature requires precise location to access Wi-Fi SSID name. Please enable precise location here or in the app settings.</string>
<string name="request">Request</string>
<string name="toggle_vpn">Toggle VPN</string>
<string name="no_tunnel_available">No tunnels available</string>
</resources>