summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2018-05-27 18:57:52 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2018-05-27 18:57:52 +0200
commit292df1217580fdda3592f59ed356bdb289564392 (patch)
treecf2ce12534656d1f5642116ce1d6402114579d4d
parentf988306c17a8acbc4e0c9784a99d63a16ad19950 (diff)
Support always-on-vpn
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--app/src/main/AndroidManifest.xml3
-rw-r--r--app/src/main/java/com/wireguard/android/BootShutdownReceiver.java6
-rw-r--r--app/src/main/java/com/wireguard/android/activity/SettingsActivity.java7
-rw-r--r--app/src/main/java/com/wireguard/android/backend/GoBackend.java18
-rw-r--r--app/src/main/java/com/wireguard/android/model/TunnelManager.java30
5 files changed, 55 insertions, 9 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f99c02ee..e83f8841 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -62,9 +62,6 @@
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
- <meta-data
- android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
- android:value="false" />
</service>
<service
diff --git a/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java b/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java
index eddfac9e..675bbb42 100644
--- a/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java
+++ b/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java
@@ -11,6 +11,7 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
+import com.wireguard.android.backend.WgQuickBackend;
import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.ExceptionLoggers;
@@ -19,13 +20,16 @@ public class BootShutdownReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
+ if (Application.getComponent().getBackendType() != WgQuickBackend.class) {
+ return;
+ }
final String action = intent.getAction();
if (action == null)
return;
final TunnelManager tunnelManager = Application.getComponent().getTunnelManager();
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
Log.i(TAG, "Broadcast receiver restoring state (boot)");
- tunnelManager.restoreState().whenComplete(ExceptionLoggers.D);
+ tunnelManager.restoreState(false).whenComplete(ExceptionLoggers.D);
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
Log.i(TAG, "Broadcast receiver saving state (shutdown)");
tunnelManager.saveState();
diff --git a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
index 2507db67..040c5efc 100644
--- a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
@@ -94,9 +94,10 @@ public class SettingsActivity extends AppCompatActivity {
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
addPreferencesFromResource(R.xml.preferences);
if (Application.getComponent().getBackendType() != WgQuickBackend.class) {
- final Preference toolsInstaller =
- getPreferenceManager().findPreference("tools_installer");
- getPreferenceScreen().removePreference(toolsInstaller);
+ Preference pref = getPreferenceManager().findPreference("tools_installer");
+ getPreferenceScreen().removePreference(pref);
+ pref = getPreferenceManager().findPreference("restore_on_boot");
+ getPreferenceScreen().removePreference(pref);
}
}
}
diff --git a/app/src/main/java/com/wireguard/android/backend/GoBackend.java b/app/src/main/java/com/wireguard/android/backend/GoBackend.java
index d11802f3..ffd05eae 100644
--- a/app/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/app/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -6,6 +6,7 @@
package com.wireguard.android.backend;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
@@ -13,9 +14,11 @@ import android.support.v4.util.ArraySet;
import android.util.Log;
import com.wireguard.android.Application;
+import com.wireguard.android.activity.MainActivity;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.model.Tunnel.Statistics;
+import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.config.Config;
import com.wireguard.config.IPCidr;
import com.wireguard.config.Interface;
@@ -158,6 +161,10 @@ public final class GoBackend implements Backend {
final VpnService.Builder builder = service.getBuilder();
builder.setSession(tunnel.getName());
+ final Intent configureIntent = new Intent(context, MainActivity.class);
+ configureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ builder.setConfigureIntent(PendingIntent.getActivity(context, 0, configureIntent, 0));
+
for (final IPCidr addr : config.getInterface().getAddresses())
builder.addAddress(addr.getAddress(), addr.getCidr());
@@ -202,6 +209,7 @@ public final class GoBackend implements Backend {
}
private void startVpnService() {
+ Log.d(TAG, "Requesting to start VpnService");
context.startService(new Intent(context, VpnService.class));
}
@@ -225,5 +233,15 @@ public final class GoBackend implements Backend {
vpnService = vpnService.newIncompleteFuture();
super.onDestroy();
}
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ vpnService.complete(this);
+ if (intent == null || intent.getComponent() == null || !intent.getComponent().getPackageName().equals(getPackageName())) {
+ Log.d(TAG, "Service started by Always-on VPN feature");
+ Application.getComponent().getTunnelManager().restoreState(true).whenComplete(ExceptionLoggers.D);
+ }
+ return super.onStartCommand(intent, flags, startId);
+ }
}
}
diff --git a/app/src/main/java/com/wireguard/android/model/TunnelManager.java b/app/src/main/java/com/wireguard/android/model/TunnelManager.java
index 92c1d99b..b1197efe 100644
--- a/app/src/main/java/com/wireguard/android/model/TunnelManager.java
+++ b/app/src/main/java/com/wireguard/android/model/TunnelManager.java
@@ -24,6 +24,7 @@ import com.wireguard.android.util.ObservableSortedKeyedArrayList;
import com.wireguard.android.util.ObservableSortedKeyedList;
import com.wireguard.config.Config;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
@@ -55,6 +56,8 @@ public final class TunnelManager extends BaseObservable {
private final ObservableSortedKeyedList<String, Tunnel> tunnels =
new ObservableSortedKeyedArrayList<>(COMPARATOR);
private Tunnel lastUsedTunnel;
+ private boolean haveLoaded;
+ private ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>();
@Inject
public TunnelManager(final AsyncWorker asyncWorker, final Backend backend,
@@ -140,12 +143,27 @@ public final class TunnelManager extends BaseObservable {
.whenComplete(ExceptionLoggers.E);
}
+ @SuppressWarnings("unchecked")
private void onTunnelsLoaded(final Iterable<String> present, final Collection<String> running) {
for (final String name : present)
addToList(name, null, running.contains(name) ? State.UP : State.DOWN);
final String lastUsedName = preferences.getString(KEY_LAST_USED_TUNNEL, null);
if (lastUsedName != null)
setLastUsedTunnel(tunnels.get(lastUsedName));
+ CompletableFuture<Void> toComplete[];
+ synchronized (delayedLoadRestoreTunnels) {
+ haveLoaded = true;
+ toComplete = delayedLoadRestoreTunnels.toArray(new CompletableFuture[delayedLoadRestoreTunnels.size()]);
+ delayedLoadRestoreTunnels.clear();
+ }
+ restoreState(true).whenComplete((v, t) -> {
+ for (final CompletableFuture<Void> f : toComplete) {
+ if (t == null)
+ f.complete(v);
+ else
+ f.completeExceptionally(t);
+ }
+ });
}
public void refreshTunnelStates() {
@@ -157,9 +175,16 @@ public final class TunnelManager extends BaseObservable {
.whenComplete(ExceptionLoggers.E);
}
- public CompletionStage<Void> restoreState() {
- if (!preferences.getBoolean(KEY_RESTORE_ON_BOOT, false))
+ public CompletionStage<Void> restoreState(boolean force) {
+ if (!force && !preferences.getBoolean(KEY_RESTORE_ON_BOOT, false))
return CompletableFuture.completedFuture(null);
+ synchronized (delayedLoadRestoreTunnels) {
+ if (!haveLoaded) {
+ CompletableFuture<Void> f = new CompletableFuture<>();
+ delayedLoadRestoreTunnels.add(f);
+ return f;
+ }
+ }
final Set<String> previouslyRunning = preferences.getStringSet(KEY_RUNNING_TUNNELS, null);
if (previouslyRunning == null)
return CompletableFuture.completedFuture(null);
@@ -236,6 +261,7 @@ public final class TunnelManager extends BaseObservable {
tunnel.onStateChanged(e == null ? newState : tunnel.getState());
if (e == null && newState == State.UP)
setLastUsedTunnel(tunnel);
+ saveState();
});
}
}