Skip to content

Commit

Permalink
android: fix Quick Settings tailscale (#358)
Browse files Browse the repository at this point in the history
-Get rid of unused stopVPN() function
-Get rid of unused ACTION_STOP_VPN intent handling; this is redundant with DISCONNECT_VPN intent
-Tile active state should only depend on ipn state, and not the results of editing the prefs with wantRunning set. It should be active iff ipn.State > Stopped

Fixes tailscale/tailscale#11920

Signed-off-by: kari-ts <kari@tailscale.com>
  • Loading branch information
kari-ts authored May 1, 2024
1 parent 0c0853a commit d330726
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 46 deletions.
25 changes: 8 additions & 17 deletions android/src/main/java/com/tailscale/ipn/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ class App : Application(), libtailscale.AppContext {
}

val dns = DnsConfig()
var autoConnect = false
var vpnReady = false
private lateinit var connectivityManager: ConnectivityManager
private lateinit var app: libtailscale.Application

Expand Down Expand Up @@ -124,7 +122,7 @@ class App : Application(), libtailscale.AppContext {
FILE_CHANNEL_ID, "File transfers", NotificationManagerCompat.IMPORTANCE_DEFAULT)
appInstance = this
applicationScope.launch {
Notifier.tileReady.collect { isTileReady -> setTileReady(isTileReady) }
Notifier.tileActive.collect { isTileReadyToBeActive -> setTileActive(isTileReadyToBeActive) }
}
}

Expand All @@ -137,7 +135,7 @@ class App : Application(), libtailscale.AppContext {
fun setWantRunning(wantRunning: Boolean) {
val callback: (Result<Ipn.Prefs>) -> Unit = { result ->
result.fold(
onSuccess = { _ -> setTileStatus(wantRunning) },
onSuccess = { },
onFailure = { error ->
Log.d("TAG", "Set want running: failed to update preferences: ${error.message}")
})
Expand Down Expand Up @@ -187,11 +185,6 @@ class App : Application(), libtailscale.AppContext {
startService(intent)
}

fun stopVPN() {
val intent = Intent(this, IPNService::class.java)
intent.setAction(IPNService.ACTION_STOP_VPN)
startService(intent)
}

// encryptToPref a byte array of data using the Jetpack Security
// library and writes it to a global encrypted preference store.
Expand Down Expand Up @@ -219,19 +212,17 @@ class App : Application(), libtailscale.AppContext {
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
}

fun setTileReady(ready: Boolean) {
fun setTileActive(ready: Boolean) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return
}
QuickToggleService.setReady(this, ready)
Log.d("App", "Set Tile Ready: ready=$ready, autoConnect=$autoConnect")
vpnReady = ready
if (ready && autoConnect) {
Log.d("App", "Set Tile Ready: $ready")
if (ready){
startVPN()
}
}

fun setTileStatus(status: Boolean) {
QuickToggleService.setStatus(this, status)
}

fun getHostname(): String {
val userConfiguredDeviceName = getUserConfiguredDeviceName()
if (!userConfiguredDeviceName.isNullOrEmpty()) return userConfiguredDeviceName
Expand Down
13 changes: 1 addition & 12 deletions android/src/main/java/com/tailscale/ipn/IPNService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,16 @@ open class IPNService : VpnService(), libtailscale.IPNService {
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && ACTION_STOP_VPN == intent.action) {
(applicationContext as App).autoConnect = false
close()
return START_NOT_STICKY
}
val app = applicationContext as App
if (intent != null && "android.net.VpnService" == intent.action) {
// Start VPN and connect to it due to Always-on VPN
val i = Intent(IPNReceiver.INTENT_CONNECT_VPN)
i.setPackage(packageName)
i.setClass(applicationContext, IPNReceiver::class.java)
sendBroadcast(i)
Libtailscale.requestVPN(this)
app.setWantRunning(true)
return START_STICKY
}
Libtailscale.requestVPN(this)
if (app.vpnReady && app.autoConnect) {
app.setWantRunning(true)
}
app.setWantRunning(true)
return START_STICKY
}

Expand Down Expand Up @@ -134,6 +124,5 @@ open class IPNService : VpnService(), libtailscale.IPNService {

companion object {
const val ACTION_REQUEST_VPN = "com.tailscale.ipn.REQUEST_VPN"
const val ACTION_STOP_VPN = "com.tailscale.ipn.STOP_VPN"
}
}
13 changes: 2 additions & 11 deletions android/src/main/java/com/tailscale/ipn/QuickToggleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
public class QuickToggleService extends TileService {
// lock protects the static fields below it.
private static final Object lock = new Object();
// Active tracks whether the VPN is active.
private static boolean active;
// Ready tracks whether the tailscale backend is
// ready to switch on/off.
private static boolean ready;
Expand All @@ -28,7 +26,7 @@ private static void updateTile(Context ctx) {
boolean act;
synchronized (lock) {
t = currentTile;
act = active && ready;
act = ready;
}
if (t == null) {
return;
Expand All @@ -48,13 +46,6 @@ static void setReady(Context ctx, boolean rdy) {
updateTile(ctx);
}

static void setStatus(Context ctx, boolean act) {
synchronized (lock) {
active = act;
}
updateTile(ctx);
}

@Override
public void onStartListening() {
synchronized (lock) {
Expand Down Expand Up @@ -92,7 +83,7 @@ public void onClick() {
private void onTileClick() {
boolean act;
synchronized (lock) {
act = active && ready;
act = ready;
}
Intent i = new Intent(act ? IPNReceiver.INTENT_DISCONNECT_VPN : IPNReceiver.INTENT_CONNECT_VPN);
i.setPackage(getPackageName());
Expand Down
4 changes: 0 additions & 4 deletions android/src/main/java/com/tailscale/ipn/StartVPNWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ public StartVPNWorker(
@Override
public Result doWork() {
App app = ((App) getApplicationContext());

// We will start the VPN from the background
app.setAutoConnect(true);
// We need to make sure we prepare the VPN Service, just in case it isn't prepared.

Intent intent = VpnService.prepare(app);
if (intent == null) {
// If null then the VPN is already prepared and/or it's just been prepared because we have permission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object Notifier {
private val decoder = Json { ignoreUnknownKeys = true }

// Global App State
val tileReady: StateFlow<Boolean> = MutableStateFlow(false)
val tileActive: StateFlow<Boolean> = MutableStateFlow(false)
val readyToPrepareVPN: StateFlow<Boolean> = MutableStateFlow(false)

// General IPN Bus State
Expand Down Expand Up @@ -82,7 +82,7 @@ object Notifier {
}
state.collect { currstate ->
readyToPrepareVPN.set(currstate > Ipn.State.Stopped)
tileReady.set(currstate >= Ipn.State.Stopped)
tileActive.set(currstate > Ipn.State.Stopped)
}
}
}
Expand Down

0 comments on commit d330726

Please sign in to comment.