summaryrefslogtreecommitdiffhomepage
path: root/app/src/main/java/com/wireguard/android/model/TunnelManager.java
blob: b6f6819db2265b7af75992e3a61f909a04dc4f31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package com.wireguard.android.model;

import android.content.SharedPreferences;
import android.util.Log;

import com.wireguard.android.Application.ApplicationScope;
import com.wireguard.android.backend.Backend;
import com.wireguard.android.configStore.ConfigStore;
import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.config.Config;

import java.util.Collections;
import java.util.Set;

import javax.inject.Inject;

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,
 */

@ApplicationScope
public final class TunnelManager {
    public static final String KEY_PRIMARY_TUNNEL = "primary_config";
    public static final String KEY_SELECTED_TUNNEL = "selected_tunnel";
    private static final String KEY_RESTORE_ON_BOOT = "restore_on_boot";
    private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
    private static final String TAG = TunnelManager.class.getSimpleName();

    private final Backend backend;
    private final ConfigStore configStore;
    private final SharedPreferences preferences;
    private final TunnelCollection tunnels = new TunnelCollection();

    @Inject
    public TunnelManager(final Backend backend, final ConfigStore configStore,
                         final SharedPreferences preferences) {
        this.backend = backend;
        this.configStore = configStore;
        this.preferences = preferences;
    }

    private Tunnel add(final String name, final Config config) {
        final Tunnel tunnel = new Tunnel(backend, configStore, name, config);
        tunnels.put(name, tunnel);
        return tunnel;
    }

    private Tunnel add(final String name) {
        return add(name, null);
    }

    public CompletionStage<Tunnel> create(final String name, final Config config) {
        Log.v(TAG, "Requested create tunnel " + name + " with config\n" + config);
        if (!Tunnel.isNameValid(name))
            return CompletableFuture.failedFuture(new IllegalArgumentException("Invalid name"));
        if (tunnels.containsKey(name)) {
            final String message = "Tunnel " + name + " already exists";
            return CompletableFuture.failedFuture(new IllegalArgumentException(message));
        }
        return configStore.create(name, config).thenApply(savedConfig -> add(name, savedConfig));
    }

    public CompletionStage<Void> delete(final Tunnel tunnel) {
        Log.v(TAG, "Requested delete tunnel " + tunnel.getName() + " state=" + tunnel.getState());
        return backend.setState(tunnel, State.DOWN)
                .thenCompose(x -> configStore.delete(tunnel.getName()))
                .thenAccept(x -> {
                    tunnels.remove(tunnel.getName());
                    if (tunnel.getName().equals(preferences.getString(KEY_PRIMARY_TUNNEL, null)))
                        preferences.edit().remove(KEY_PRIMARY_TUNNEL).apply();
                });
    }

    public TunnelCollection getTunnels() {
        return tunnels;
    }

    public void onCreate() {
        Log.v(TAG, "onCreate triggered");
        configStore.enumerate()
                .thenApply(names -> StreamSupport.stream(names)
                        .map(this::add)
                        .map(Tunnel::getStateAsync)
                        .toArray(CompletableFuture[]::new))
                .thenCompose(CompletableFuture::allOf)
                .whenComplete(ExceptionLoggers.E);
    }

    public CompletionStage<Void> restoreState() {
        if (!preferences.getBoolean(KEY_RESTORE_ON_BOOT, false))
            return CompletableFuture.completedFuture(null);
        final Set<String> tunnelsToEnable =
                preferences.getStringSet(KEY_RUNNING_TUNNELS, Collections.emptySet());
        final CompletableFuture[] futures = StreamSupport.stream(tunnelsToEnable)
                .map(tunnels::get)
                .map(tunnel -> tunnel.setState(State.UP))
                .toArray(CompletableFuture[]::new);
        return CompletableFuture.allOf(futures);
    }

    public CompletionStage<Void> saveState() {
        final Set<String> runningTunnels = StreamSupport.stream(tunnels.values())
                .filter(tunnel -> tunnel.getState() == State.UP)
                .map(Tunnel::getName)
                .collect(Collectors.toUnmodifiableSet());
        preferences.edit().putStringSet(KEY_RUNNING_TUNNELS, runningTunnels).apply();
        return CompletableFuture.completedFuture(null);
    }
}