Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions android/src/main/java/com/tailscale/ipn/IPNReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import androidx.work.Data;

import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkManager;

import java.util.Objects;
Expand All @@ -20,26 +22,56 @@ public class IPNReceiver extends BroadcastReceiver {

public static final String INTENT_CONNECT_VPN = "com.tailscale.ipn.CONNECT_VPN";
public static final String INTENT_DISCONNECT_VPN = "com.tailscale.ipn.DISCONNECT_VPN";

private static final String INTENT_USE_EXIT_NODE = "com.tailscale.ipn.USE_EXIT_NODE";

// Unique work names prevent connect/disconnect flapping from enqueuing a long backlog.
private static final String WORK_CONNECT = "ipn-connect-vpn";
private static final String WORK_DISCONNECT = "ipn-disconnect-vpn";
private static final String WORK_USE_EXIT_NODE = "ipn-use-exit-node";

@Override
public void onReceive(Context context, Intent intent) {
WorkManager workManager = WorkManager.getInstance(context);
if (intent == null) return;

// On the relevant action, start the relevant worker, which can stay active for longer than this receiver can.
if (Objects.equals(intent.getAction(), INTENT_CONNECT_VPN)) {
workManager.enqueue(new OneTimeWorkRequest.Builder(StartVPNWorker.class).build());
} else if (Objects.equals(intent.getAction(), INTENT_DISCONNECT_VPN)) {
workManager.enqueue(new OneTimeWorkRequest.Builder(StopVPNWorker.class).build());
}
else if (Objects.equals(intent.getAction(), INTENT_USE_EXIT_NODE)) {
final WorkManager workManager = WorkManager.getInstance(context);
final String action = intent.getAction();

if (Objects.equals(action, INTENT_CONNECT_VPN)) {
OneTimeWorkRequest req =
new OneTimeWorkRequest.Builder(StartVPNWorker.class)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.addTag(WORK_CONNECT)
.build();

workManager.enqueueUniqueWork(WORK_CONNECT, ExistingWorkPolicy.REPLACE, req);

} else if (Objects.equals(action, INTENT_DISCONNECT_VPN)) {
OneTimeWorkRequest req =
new OneTimeWorkRequest.Builder(StopVPNWorker.class)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.addTag(WORK_DISCONNECT)
.build();

workManager.enqueueUniqueWork(WORK_DISCONNECT, ExistingWorkPolicy.REPLACE, req);

} else if (Objects.equals(action, INTENT_USE_EXIT_NODE)) {
String exitNode = intent.getStringExtra("exitNode");
boolean allowLanAccess = intent.getBooleanExtra("allowLanAccess", false);
Data.Builder workData = new Data.Builder();
workData.putString(UseExitNodeWorker.EXIT_NODE_NAME, exitNode);
workData.putBoolean(UseExitNodeWorker.ALLOW_LAN_ACCESS, allowLanAccess);
workManager.enqueue(new OneTimeWorkRequest.Builder(UseExitNodeWorker.class).setInputData(workData.build()).build());

Data input =
new Data.Builder()
.putString(UseExitNodeWorker.EXIT_NODE_NAME, exitNode)
.putBoolean(UseExitNodeWorker.ALLOW_LAN_ACCESS, allowLanAccess)
.build();

OneTimeWorkRequest req =
new OneTimeWorkRequest.Builder(UseExitNodeWorker.class)
.setInputData(input)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.addTag(WORK_USE_EXIT_NODE)
.build();

workManager.enqueueUniqueWork(WORK_USE_EXIT_NODE, ExistingWorkPolicy.REPLACE, req);
}
}
}
4 changes: 2 additions & 2 deletions android/src/main/java/com/tailscale/ipn/IPNService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ open class IPNService : VpnService(), libtailscale.IPNService {
START_NOT_STICKY
}
ACTION_START_VPN -> {
scope.launch { showForegroundNotification() }
showForegroundNotification()
app.setWantRunning(true)
Libtailscale.requestVPN(this)
START_STICKY
Expand All @@ -78,7 +78,7 @@ open class IPNService : VpnService(), libtailscale.IPNService {
// This means that we were restarted after the service was killed
// (potentially due to OOM).
if (UninitializedApp.get().isAbleToStartVPN()) {
scope.launch { showForegroundNotification() }
showForegroundNotification()
App.get()
Libtailscale.requestVPN(this)
START_STICKY
Expand Down