diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2018-05-27 18:57:52 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2018-05-27 18:57:52 +0200 |
commit | 292df1217580fdda3592f59ed356bdb289564392 (patch) | |
tree | cf2ce12534656d1f5642116ce1d6402114579d4d | |
parent | f988306c17a8acbc4e0c9784a99d63a16ad19950 (diff) |
Support always-on-vpn
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
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(); }); } } |