android: check if other VPN is active (#475)

Detect when another VPN is active and launch dialog giving user the option to navigate to settings to disable.
Update state string and toggle to require successful VPN preparation

To do in a follow-up: monitor VPN connection, and if Tailscale VPN disconnects due to another VPN connecting, update toggle and text
Updates tailscale/tailscale#12850

Signed-off-by: kari-ts <kari@tailscale.com>
pull/476/head
kari-ts 3 months ago committed by GitHub
parent 10a4350c02
commit 1a41ab3b66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -5,12 +5,15 @@ package com.tailscale.ipn
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.RestrictionsManager
import android.content.pm.ActivityInfo
import android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE
import android.content.res.Configuration.SCREENLAYOUT_SIZE_MASK
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Bundle
import android.provider.Settings
import android.util.Log
@ -129,8 +132,13 @@ class MainActivity : ComponentActivity() {
vpnViewModel.setVpnPrepared(true)
App.get().startVPN()
} else {
Log.d("VpnPermission", "VPN permission denied")
vpnViewModel.setVpnPrepared(false)
if (isAnotherVpnActive(this)) {
Log.d("VpnPermission", "Another VPN is likely active")
showOtherVPNConflictDialog()
} else {
Log.d("VpnPermission", "Permission was denied by the user")
viewModel.setVpnPrepared(false)
}
}
}
viewModel.setVpnPermissionLauncher(vpnPermissionLauncher)
@ -285,6 +293,34 @@ class MainActivity : ComponentActivity() {
lifecycleScope.launch { Notifier.loginFinished.collect { _ -> loginQRCode.set(null) } }
}
private fun showOtherVPNConflictDialog() {
AlertDialog.Builder(this)
.setTitle(R.string.vpn_permission_denied)
.setMessage(R.string.multiple_vpn_explainer)
.setPositiveButton(R.string.go_to_settings) { _, _ ->
// Intent to open the VPN settings
val intent = Intent(Settings.ACTION_VPN_SETTINGS)
startActivity(intent)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
fun isAnotherVpnActive(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
if (activeNetwork != null) {
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
if (networkCapabilities != null &&
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
return true
}
}
return false
}
// Returns true if we should render a QR code instead of launching a browser
// for login requests
private fun useQRCodeLogin(): Boolean {

@ -100,8 +100,10 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() {
val isOn =
when {
currentState == State.Running || currentState == State.Starting -> true
previousState == State.NoState && currentState == State.Starting -> true
prepared && currentState == State.Running || currentState == State.Starting ->
true
previousState == State.NoState && currentState == State.Starting ->
true
else -> false
}
@ -182,7 +184,7 @@ private fun userStringRes(currentState: State?, previousState: State?, vpnPrepar
currentState == State.NeedsMachineAuth -> R.string.needs_machine_auth
currentState == State.Stopped -> R.string.stopped
currentState == State.Starting -> R.string.starting
currentState == State.Running -> R.string.connected
currentState == State.Running -> if (vpnPrepared) R.string.connected else R.string.placeholder
else -> R.string.placeholder
}
}

@ -292,4 +292,11 @@
<string name="health_warnings">Health warnings</string>
<string name="no_issues_found">No issues found</string>
<string name="tailscale_is_operating_normally">Tailscale is operating normally.</string>
<!-- Strings for the multiple VPNs dialog -->
<string name="vpn_permission_denied">VPN permission denied</string>
<string name="multiple_vpn_explainer">Only one VPN can be active, and it appears another is already running. Before starting Tailscale, disable the other VPN.</string>
<string name="go_to_settings">Go to Settings</string>
<string name="cancel">Cancel</string>
</resources>

Loading…
Cancel
Save