diff options
5 files changed, 64 insertions, 6 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java index 9695aab7..53fe3d42 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java @@ -9,6 +9,7 @@ import androidx.annotation.Nullable; import android.content.Context; import android.util.Log; +import android.util.Pair; import com.wireguard.android.backend.BackendException.Reason; import com.wireguard.android.backend.Tunnel.State; @@ -23,6 +24,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -44,6 +46,7 @@ public final class WgQuickBackend implements Backend { private final ToolsInstaller toolsInstaller; private final File localTemporaryDir; private final Map<Tunnel, Config> runningConfigs = new HashMap<>(); + private boolean multipleTunnels; public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) { localTemporaryDir = new File(context.getCacheDir(), "tmp"); @@ -51,6 +54,10 @@ public final class WgQuickBackend implements Backend { this.toolsInstaller = toolsInstaller; } + public void setMultipleTunnels(boolean on) { + multipleTunnels = on; + } + @Override public Set<String> getRunningTunnelNames() { final List<String> output = new ArrayList<>(); @@ -106,6 +113,7 @@ public final class WgQuickBackend implements Backend { public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception { final State originalState = getState(tunnel); final Config originalConfig = runningConfigs.get(tunnel); + final Map<Tunnel, Config> runningConfigsSnapshot = new HashMap<>(runningConfigs); if (state == State.TOGGLE) state = originalState == State.UP ? State.DOWN : State.UP; @@ -114,13 +122,37 @@ public final class WgQuickBackend implements Backend { return originalState; if (state == State.UP) { toolsInstaller.ensureToolsAvailable(); + if (!multipleTunnels && originalState == State.DOWN) { + final List<Pair<Tunnel, Config>> rewind = new LinkedList<>(); + try { + for (final Map.Entry<Tunnel, Config> entry : runningConfigsSnapshot.entrySet()) { + setStateInternal(entry.getKey(), entry.getValue(), State.DOWN); + rewind.add(Pair.create(entry.getKey(), entry.getValue())); + } + } catch (final Exception e) { + try { + for (final Pair<Tunnel, Config> entry : rewind) { + setStateInternal(entry.first, entry.second, State.UP); + } + } catch (final Exception ignored) { } + throw e; + } + } if (originalState == State.UP) setStateInternal(tunnel, originalConfig == null ? config : originalConfig, State.DOWN); try { setStateInternal(tunnel, config, State.UP); - } catch(final Exception e) { - if (originalState == State.UP && originalConfig != null) - setStateInternal(tunnel, originalConfig, State.UP); + } catch (final Exception e) { + try { + if (originalState == State.UP && originalConfig != null) { + setStateInternal(tunnel, originalConfig, State.UP); + } + if (!multipleTunnels && originalState == State.DOWN) { + for (final Map.Entry<Tunnel, Config> entry : runningConfigsSnapshot.entrySet()) { + setStateInternal(entry.getKey(), entry.getValue(), State.UP); + } + } + } catch (final Exception ignored) { } throw e; } } else if (state == State.DOWN) { diff --git a/ui/src/main/java/com/wireguard/android/Application.java b/ui/src/main/java/com/wireguard/android/Application.java index 2ebeb69d..655c6b21 100644 --- a/ui/src/main/java/com/wireguard/android/Application.java +++ b/ui/src/main/java/com/wireguard/android/Application.java @@ -35,7 +35,7 @@ import java.util.Locale; import java9.util.concurrent.CompletableFuture; -public class Application extends android.app.Application { +public class Application extends android.app.Application implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = "WireGuard/" + Application.class.getSimpleName(); public static final String USER_AGENT; @@ -86,7 +86,9 @@ public class Application extends android.app.Application { try { if (!didStartRootShell) app.rootShell.start(); - backend = new WgQuickBackend(app.getApplicationContext(), app.rootShell, app.toolsInstaller); + WgQuickBackend wgQuickBackend = new WgQuickBackend(app.getApplicationContext(), app.rootShell, app.toolsInstaller); + wgQuickBackend.setMultipleTunnels(app.sharedPreferences.getBoolean("multiple_tunnels", false)); + backend = wgQuickBackend; } catch (final Exception ignored) { } } @@ -167,5 +169,19 @@ public class Application extends android.app.Application { tunnelManager.onCreate(); asyncWorker.supplyAsync(Application::getBackend).thenAccept(futureBackend::complete); + + sharedPreferences.registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onTerminate() { + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + super.onTerminate(); + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) { + if ("multiple_tunnels".equals(key) && backend != null && backend instanceof WgQuickBackend) + ((WgQuickBackend)backend).setMultipleTunnels(sharedPreferences.getBoolean(key, false)); } } diff --git a/ui/src/main/java/com/wireguard/android/activity/SettingsActivity.java b/ui/src/main/java/com/wireguard/android/activity/SettingsActivity.java index f545c371..b597a9b1 100644 --- a/ui/src/main/java/com/wireguard/android/activity/SettingsActivity.java +++ b/ui/src/main/java/com/wireguard/android/activity/SettingsActivity.java @@ -99,7 +99,8 @@ public class SettingsActivity extends ThemeChangeAwareActivity { final Preference wgQuickOnlyPrefs[] = { getPreferenceManager().findPreference("tools_installer"), - getPreferenceManager().findPreference("restore_on_boot") + getPreferenceManager().findPreference("restore_on_boot"), + getPreferenceManager().findPreference("multiple_tunnels") }; for (final Preference pref : wgQuickOnlyPrefs) pref.setVisible(false); diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 45964eec..5df74017 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -104,6 +104,9 @@ <string name="module_installer_working">Downloading and installing…</string> <string name="module_installer_error">Something went wrong. Please try again</string> <string name="mtu">MTU</string> + <string name="multiple_tunnels_title">Allow multiple simultaneous tunnels</string> + <string name="multiple_tunnels_summary_on">Multiple tunnels may be turned on simultaneously</string> + <string name="multiple_tunnels_summary_off">Turning on one tunnel will turn off others</string> <string name="name">Name</string> <string name="no_config_error">Trying to bring up a tunnel with no config</string> <string name="no_configs_error">No configurations found</string> diff --git a/ui/src/main/res/xml/preferences.xml b/ui/src/main/res/xml/preferences.xml index 9c09ae89..6899edce 100644 --- a/ui/src/main/res/xml/preferences.xml +++ b/ui/src/main/res/xml/preferences.xml @@ -8,6 +8,12 @@ android:title="@string/restore_on_boot_title" /> <com.wireguard.android.preference.ModuleDownloaderPreference android:key="module_downloader" /> <com.wireguard.android.preference.ToolsInstallerPreference android:key="tools_installer" /> + <CheckBoxPreference + android:defaultValue="false" + android:key="multiple_tunnels" + android:summaryOff="@string/multiple_tunnels_summary_off" + android:summaryOn="@string/multiple_tunnels_summary_on" + android:title="@string/multiple_tunnels_title" /> <com.wireguard.android.preference.ZipExporterPreference /> <com.wireguard.android.preference.LogExporterPreference /> <CheckBoxPreference |