diff --git a/app/src/main/java/com/wireguard/android/backend/Backend.java b/app/src/main/java/com/wireguard/android/backend/Backend.java index c44f626..fc174be 100644 --- a/app/src/main/java/com/wireguard/android/backend/Backend.java +++ b/app/src/main/java/com/wireguard/android/backend/Backend.java @@ -5,6 +5,8 @@ import com.wireguard.android.model.Tunnel.State; import com.wireguard.android.model.Tunnel.Statistics; import com.wireguard.config.Config; +import java.util.Set; + import java9.util.concurrent.CompletionStage; /** @@ -25,6 +27,14 @@ public interface Backend { */ CompletionStage applyConfig(Tunnel tunnel, Config config); + /** + * Enumerate the names of currently-running tunnels. + * + * @return A future completed when the set of running tunnel names is available. This future + * will always be completed on the main thread. + */ + CompletionStage> enumerate(); + /** * Get the actual state of a tunnel, asynchronously. * diff --git a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java index af6bc77..03daee8 100644 --- a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java +++ b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java @@ -12,12 +12,15 @@ import com.wireguard.config.Config; import java.io.File; import java.io.IOException; -import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java9.util.concurrent.CompletableFuture; import java9.util.concurrent.CompletionStage; +import java9.util.stream.Collectors; +import java9.util.stream.Stream; /** * Created by samuel on 12/19/17. @@ -53,26 +56,24 @@ public final class WgQuickBackend implements Backend { } @Override - public CompletionStage getState(final Tunnel tunnel) { - Log.v(TAG, "Requested state for tunnel " + tunnel.getName()); + public CompletionStage> enumerate() { return asyncWorker.supplyAsync(() -> { final List output = new LinkedList<>(); - final State state; - if (rootShell.run(output, "wg show interfaces") != 0) { - state = State.UNKNOWN; - } else if (output.isEmpty()) { - // There are no running interfaces. - state = State.DOWN; - } else { - // wg puts all interface names on the same line. Split them into separate elements. - final String[] names = output.get(0).split(" "); - state = Arrays.asList(names).contains(tunnel.getName()) ? State.UP : State.DOWN; - } - Log.v(TAG, "Got state " + state + " for tunnel " + tunnel.getName()); - return state; + // Don't throw an exception here or nothing will show up in the UI. + if (rootShell.run(output, "wg show interfaces") != 0 || output.isEmpty()) + return Collections.emptySet(); + // wg puts all interface names on the same line. Split them into separate elements. + return Stream.of(output.get(0).split(" ")) + .collect(Collectors.toUnmodifiableSet()); }); } + @Override + public CompletionStage getState(final Tunnel tunnel) { + Log.v(TAG, "Requested state for tunnel " + tunnel.getName()); + return enumerate().thenApply(set -> set.contains(tunnel.getName()) ? State.UP : State.DOWN); + } + @Override public CompletionStage getStatistics(final Tunnel tunnel) { return CompletableFuture.completedFuture(new Statistics());