diff options
Diffstat (limited to 'app/src/main/java/com/wireguard/android/model')
3 files changed, 0 insertions, 512 deletions
diff --git a/app/src/main/java/com/wireguard/android/model/ApplicationData.java b/app/src/main/java/com/wireguard/android/model/ApplicationData.java deleted file mode 100644 index 65edff90..00000000 --- a/app/src/main/java/com/wireguard/android/model/ApplicationData.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.model; - -import androidx.databinding.BaseObservable; -import androidx.databinding.Bindable; -import android.graphics.drawable.Drawable; - -import com.wireguard.android.BR; -import com.wireguard.util.Keyed; - -public class ApplicationData extends BaseObservable implements Keyed<String> { - private final Drawable icon; - private final String name; - private final String packageName; - private boolean excludedFromTunnel; - - public ApplicationData(final Drawable icon, final String name, final String packageName, final boolean excludedFromTunnel) { - this.icon = icon; - this.name = name; - this.packageName = packageName; - this.excludedFromTunnel = excludedFromTunnel; - } - - public Drawable getIcon() { - return icon; - } - - @Override - public String getKey() { - return name; - } - - public String getName() { - return name; - } - - public String getPackageName() { - return packageName; - } - - @Bindable - public boolean isExcludedFromTunnel() { - return excludedFromTunnel; - } - - public void setExcludedFromTunnel(final boolean excludedFromTunnel) { - this.excludedFromTunnel = excludedFromTunnel; - notifyPropertyChanged(BR.excludedFromTunnel); - } -} diff --git a/app/src/main/java/com/wireguard/android/model/Tunnel.java b/app/src/main/java/com/wireguard/android/model/Tunnel.java deleted file mode 100644 index 49e78a22..00000000 --- a/app/src/main/java/com/wireguard/android/model/Tunnel.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.model; - -import androidx.databinding.BaseObservable; -import androidx.databinding.Bindable; -import androidx.annotation.Nullable; - -import com.wireguard.android.BR; -import com.wireguard.android.util.ExceptionLoggers; -import com.wireguard.config.Config; -import com.wireguard.util.Keyed; - -import java.util.regex.Pattern; - -import java9.util.concurrent.CompletableFuture; -import java9.util.concurrent.CompletionStage; - -/** - * Encapsulates the volatile and nonvolatile state of a WireGuard tunnel. - */ - -public class Tunnel extends BaseObservable implements Keyed<String> { - public static final int NAME_MAX_LENGTH = 15; - private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}"); - - private final TunnelManager manager; - @Nullable private Config config; - private String name; - private State state; - @Nullable private Statistics statistics; - - Tunnel(final TunnelManager manager, final String name, - @Nullable final Config config, final State state) { - this.manager = manager; - this.name = name; - this.config = config; - this.state = state; - } - - public static boolean isNameInvalid(final CharSequence name) { - return !NAME_PATTERN.matcher(name).matches(); - } - - public CompletionStage<Void> delete() { - return manager.delete(this); - } - - @Bindable - @Nullable - public Config getConfig() { - if (config == null) - manager.getTunnelConfig(this).whenComplete(ExceptionLoggers.E); - return config; - } - - public CompletionStage<Config> getConfigAsync() { - if (config == null) - return manager.getTunnelConfig(this); - return CompletableFuture.completedFuture(config); - } - - @Override - public String getKey() { - return name; - } - - @Bindable - public String getName() { - return name; - } - - @Bindable - public State getState() { - return state; - } - - public CompletionStage<State> getStateAsync() { - return TunnelManager.getTunnelState(this); - } - - @Bindable - @Nullable - public Statistics getStatistics() { - // FIXME: Check age of statistics. - if (statistics == null) - TunnelManager.getTunnelStatistics(this).whenComplete(ExceptionLoggers.E); - return statistics; - } - - public CompletionStage<Statistics> getStatisticsAsync() { - // FIXME: Check age of statistics. - if (statistics == null) - return TunnelManager.getTunnelStatistics(this); - return CompletableFuture.completedFuture(statistics); - } - - Config onConfigChanged(final Config config) { - this.config = config; - notifyPropertyChanged(BR.config); - return config; - } - - public String onNameChanged(final String name) { - this.name = name; - notifyPropertyChanged(BR.name); - return name; - } - - State onStateChanged(final State state) { - if (state != State.UP) - onStatisticsChanged(null); - this.state = state; - notifyPropertyChanged(BR.state); - return state; - } - - @Nullable - Statistics onStatisticsChanged(@Nullable final Statistics statistics) { - this.statistics = statistics; - notifyPropertyChanged(BR.statistics); - return statistics; - } - - public CompletionStage<Config> setConfig(final Config config) { - if (!config.equals(this.config)) - return manager.setTunnelConfig(this, config); - return CompletableFuture.completedFuture(this.config); - } - - public CompletionStage<String> setName(final String name) { - if (!name.equals(this.name)) - return manager.setTunnelName(this, name); - return CompletableFuture.completedFuture(this.name); - } - - public CompletionStage<State> setState(final State state) { - if (state != this.state) - return manager.setTunnelState(this, state); - return CompletableFuture.completedFuture(this.state); - } - - public enum State { - DOWN, - TOGGLE, - UP; - - public static State of(final boolean running) { - return running ? UP : DOWN; - } - } - - public static class Statistics extends BaseObservable { - } -} diff --git a/app/src/main/java/com/wireguard/android/model/TunnelManager.java b/app/src/main/java/com/wireguard/android/model/TunnelManager.java deleted file mode 100644 index 2deea6df..00000000 --- a/app/src/main/java/com/wireguard/android/model/TunnelManager.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package com.wireguard.android.model; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import androidx.databinding.BaseObservable; -import androidx.databinding.Bindable; -import androidx.annotation.Nullable; - -import com.wireguard.android.Application; -import com.wireguard.android.BR; -import com.wireguard.android.R; -import com.wireguard.android.configStore.ConfigStore; -import com.wireguard.android.model.Tunnel.State; -import com.wireguard.android.model.Tunnel.Statistics; -import com.wireguard.android.util.ExceptionLoggers; -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; - -import java9.util.Comparators; -import java9.util.concurrent.CompletableFuture; -import java9.util.concurrent.CompletionStage; -import java9.util.stream.Collectors; -import java9.util.stream.StreamSupport; - -/** - * Maintains and mediates changes to the set of available WireGuard tunnels, - */ - -public final class TunnelManager extends BaseObservable { - private static final Comparator<String> COMPARATOR = Comparators.<String>thenComparing( - String.CASE_INSENSITIVE_ORDER, Comparators.naturalOrder()); - private static final String KEY_LAST_USED_TUNNEL = "last_used_tunnel"; - private static final String KEY_RESTORE_ON_BOOT = "restore_on_boot"; - private static final String KEY_RUNNING_TUNNELS = "enabled_configs"; - - private final CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> completableTunnels = new CompletableFuture<>(); - private final ConfigStore configStore; - private final Context context = Application.get(); - private final ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>(); - private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR); - private boolean haveLoaded; - @Nullable private Tunnel lastUsedTunnel; - - public TunnelManager(final ConfigStore configStore) { - this.configStore = configStore; - } - - static CompletionStage<State> getTunnelState(final Tunnel tunnel) { - return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getState(tunnel)) - .thenApply(tunnel::onStateChanged); - } - - static CompletionStage<Statistics> getTunnelStatistics(final Tunnel tunnel) { - return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getStatistics(tunnel)) - .thenApply(tunnel::onStatisticsChanged); - } - - private Tunnel addToList(final String name, @Nullable final Config config, final State state) { - final Tunnel tunnel = new Tunnel(this, name, config, state); - tunnels.add(tunnel); - return tunnel; - } - - public CompletionStage<Tunnel> create(final String name, @Nullable final Config config) { - if (Tunnel.isNameInvalid(name)) - return CompletableFuture.failedFuture(new IllegalArgumentException(context.getString(R.string.tunnel_error_invalid_name))); - if (tunnels.containsKey(name)) { - final String message = context.getString(R.string.tunnel_error_already_exists, name); - return CompletableFuture.failedFuture(new IllegalArgumentException(message)); - } - return Application.getAsyncWorker().supplyAsync(() -> configStore.create(name, config)) - .thenApply(savedConfig -> addToList(name, savedConfig, State.DOWN)); - } - - CompletionStage<Void> delete(final Tunnel tunnel) { - final State originalState = tunnel.getState(); - final boolean wasLastUsed = tunnel == lastUsedTunnel; - // Make sure nothing touches the tunnel. - if (wasLastUsed) - setLastUsedTunnel(null); - tunnels.remove(tunnel); - return Application.getAsyncWorker().runAsync(() -> { - if (originalState == State.UP) - Application.getBackend().setState(tunnel, State.DOWN); - try { - configStore.delete(tunnel.getName()); - } catch (final Exception e) { - if (originalState == State.UP) - Application.getBackend().setState(tunnel, State.UP); - // Re-throw the exception to fail the completion. - throw e; - } - }).whenComplete((x, e) -> { - if (e == null) - return; - // Failure, put the tunnel back. - tunnels.add(tunnel); - if (wasLastUsed) - setLastUsedTunnel(tunnel); - }); - } - - @Bindable - @Nullable - public Tunnel getLastUsedTunnel() { - return lastUsedTunnel; - } - - CompletionStage<Config> getTunnelConfig(final Tunnel tunnel) { - return Application.getAsyncWorker().supplyAsync(() -> configStore.load(tunnel.getName())) - .thenApply(tunnel::onConfigChanged); - } - - public CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> getTunnels() { - return completableTunnels; - } - - public void onCreate() { - Application.getAsyncWorker().supplyAsync(configStore::enumerate) - .thenAcceptBoth(Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().enumerate()), this::onTunnelsLoaded) - .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 = Application.getSharedPreferences().getString(KEY_LAST_USED_TUNNEL, null); - if (lastUsedName != null) - setLastUsedTunnel(tunnels.get(lastUsedName)); - final 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); - } - }); - - completableTunnels.complete(tunnels); - } - - public void refreshTunnelStates() { - Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().enumerate()) - .thenAccept(running -> { - for (final Tunnel tunnel : tunnels) - tunnel.onStateChanged(running.contains(tunnel.getName()) ? State.UP : State.DOWN); - }) - .whenComplete(ExceptionLoggers.E); - } - - public CompletionStage<Void> restoreState(final boolean force) { - if (!force && !Application.getSharedPreferences().getBoolean(KEY_RESTORE_ON_BOOT, false)) - return CompletableFuture.completedFuture(null); - synchronized (delayedLoadRestoreTunnels) { - if (!haveLoaded) { - final CompletableFuture<Void> f = new CompletableFuture<>(); - delayedLoadRestoreTunnels.add(f); - return f; - } - } - final Set<String> previouslyRunning = Application.getSharedPreferences().getStringSet(KEY_RUNNING_TUNNELS, null); - if (previouslyRunning == null) - return CompletableFuture.completedFuture(null); - return CompletableFuture.allOf(StreamSupport.stream(tunnels) - .filter(tunnel -> previouslyRunning.contains(tunnel.getName())) - .map(tunnel -> setTunnelState(tunnel, State.UP)) - .toArray(CompletableFuture[]::new)); - } - - public void saveState() { - final Set<String> runningTunnels = StreamSupport.stream(tunnels) - .filter(tunnel -> tunnel.getState() == State.UP) - .map(Tunnel::getName) - .collect(Collectors.toUnmodifiableSet()); - Application.getSharedPreferences().edit().putStringSet(KEY_RUNNING_TUNNELS, runningTunnels).apply(); - } - - private void setLastUsedTunnel(@Nullable final Tunnel tunnel) { - if (tunnel == lastUsedTunnel) - return; - lastUsedTunnel = tunnel; - notifyPropertyChanged(BR.lastUsedTunnel); - if (tunnel != null) - Application.getSharedPreferences().edit().putString(KEY_LAST_USED_TUNNEL, tunnel.getName()).apply(); - else - Application.getSharedPreferences().edit().remove(KEY_LAST_USED_TUNNEL).apply(); - } - - CompletionStage<Config> setTunnelConfig(final Tunnel tunnel, final Config config) { - return Application.getAsyncWorker().supplyAsync(() -> { - final Config appliedConfig = Application.getBackend().applyConfig(tunnel, config); - return configStore.save(tunnel.getName(), appliedConfig); - }).thenApply(tunnel::onConfigChanged); - } - - CompletionStage<String> setTunnelName(final Tunnel tunnel, final String name) { - if (Tunnel.isNameInvalid(name)) - return CompletableFuture.failedFuture(new IllegalArgumentException(context.getString(R.string.tunnel_error_invalid_name))); - if (tunnels.containsKey(name)) { - final String message = context.getString(R.string.tunnel_error_already_exists, name); - return CompletableFuture.failedFuture(new IllegalArgumentException(message)); - } - final State originalState = tunnel.getState(); - final boolean wasLastUsed = tunnel == lastUsedTunnel; - // Make sure nothing touches the tunnel. - if (wasLastUsed) - setLastUsedTunnel(null); - tunnels.remove(tunnel); - return Application.getAsyncWorker().supplyAsync(() -> { - if (originalState == State.UP) - Application.getBackend().setState(tunnel, State.DOWN); - configStore.rename(tunnel.getName(), name); - final String newName = tunnel.onNameChanged(name); - if (originalState == State.UP) - Application.getBackend().setState(tunnel, State.UP); - return newName; - }).whenComplete((newName, e) -> { - // On failure, we don't know what state the tunnel might be in. Fix that. - if (e != null) - getTunnelState(tunnel); - // Add the tunnel back to the manager, under whatever name it thinks it has. - tunnels.add(tunnel); - if (wasLastUsed) - setLastUsedTunnel(tunnel); - }); - } - - CompletionStage<State> setTunnelState(final Tunnel tunnel, final State state) { - // Ensure the configuration is loaded before trying to use it. - return tunnel.getConfigAsync().thenCompose(x -> - Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().setState(tunnel, state)) - ).whenComplete((newState, e) -> { - // Ensure onStateChanged is always called (failure or not), and with the correct state. - tunnel.onStateChanged(e == null ? newState : tunnel.getState()); - if (e == null && newState == State.UP) - setLastUsedTunnel(tunnel); - saveState(); - }); - } - - public static final class IntentReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, @Nullable final Intent intent) { - final TunnelManager manager = Application.getTunnelManager(); - if (intent == null) - return; - final String action = intent.getAction(); - if (action == null) - return; - - if ("com.wireguard.android.action.REFRESH_TUNNEL_STATES".equals(action)) { - manager.refreshTunnelStates(); - return; - } - - /* We disable the below, for now, as the security model of allowing this - * might take a bit more consideration. - */ - if (true) - return; - - final State state; - if ("com.wireguard.android.action.SET_TUNNEL_UP".equals(action)) - state = State.UP; - else if ("com.wireguard.android.action.SET_TUNNEL_DOWN".equals(action)) - state = State.DOWN; - else - return; - - final String tunnelName = intent.getStringExtra("tunnel"); - if (tunnelName == null) - return; - manager.getTunnels().thenAccept(tunnels -> { - final Tunnel tunnel = tunnels.get(tunnelName); - if (tunnel == null) - return; - manager.setTunnelState(tunnel, state); - }); - } - } -} |