Serviceless rewrite, part 1

Signed-off-by: Samuel Holland <samuel@sholland.org>
This commit is contained in:
Samuel Holland
2018-01-06 04:09:29 -06:00
parent 4c0869393e
commit 609194fae2
68 changed files with 2564 additions and 2296 deletions
@@ -0,0 +1,166 @@
package com.wireguard.android.model;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.wireguard.android.BR;
import com.wireguard.android.backend.Backend;
import com.wireguard.android.configStore.ConfigStore;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.config.Config;
import org.threeten.bp.Instant;
import java.util.Objects;
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 Comparable<Tunnel> {
public static final int NAME_MAX_LENGTH = 16;
private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,16}");
private static final String TAG = Tunnel.class.getSimpleName();
private final Backend backend;
private final ConfigStore configStore;
private final String name;
private Config config;
private Instant lastStateChange = Instant.EPOCH;
private State state = State.UNKNOWN;
private Statistics statistics;
Tunnel(@NonNull final Backend backend, @NonNull final ConfigStore configStore,
@NonNull final String name, @Nullable final Config config) {
this.backend = backend;
this.configStore = configStore;
this.name = name;
this.config = config;
}
public static boolean isNameValid(final CharSequence name) {
return name != null && NAME_PATTERN.matcher(name).matches();
}
@Override
public int compareTo(@NonNull final Tunnel tunnel) {
return name.compareTo(tunnel.name);
}
@Bindable
public Config getConfig() {
if (config == null)
getConfigAsync().whenComplete(ExceptionLoggers.D);
return config;
}
public CompletionStage<Config> getConfigAsync() {
if (config == null)
return configStore.load(name).thenApply(this::setConfigInternal);
return CompletableFuture.completedFuture(config);
}
@Bindable
public Instant getLastStateChange() {
return lastStateChange;
}
@Bindable
public String getName() {
return name;
}
@Bindable
public State getState() {
if (state == State.UNKNOWN)
getStateAsync().whenComplete(ExceptionLoggers.D);
return state;
}
public CompletionStage<State> getStateAsync() {
if (state == State.UNKNOWN)
return backend.getState(this).thenApply(this::setStateInternal);
return CompletableFuture.completedFuture(state);
}
@Bindable
public Statistics getStatistics() {
// FIXME: Check age of statistics.
if (statistics == null)
getStatisticsAsync().whenComplete(ExceptionLoggers.D);
return statistics;
}
public CompletionStage<Statistics> getStatisticsAsync() {
// FIXME: Check age of statistics.
if (statistics == null)
return backend.getStatistics(this).thenApply(this::setStatisticsInternal);
return CompletableFuture.completedFuture(statistics);
}
private void onStateChanged(final State oldState, final State newState) {
if (oldState != State.UNKNOWN) {
lastStateChange = Instant.now();
notifyPropertyChanged(BR.lastStateChange);
}
if (newState != State.UP)
setStatisticsInternal(null);
}
public CompletionStage<Config> setConfig(@NonNull final Config config) {
if (!config.equals(this.config)) {
return backend.applyConfig(this, config)
.thenCompose(cfg -> configStore.save(name, cfg))
.thenApply(this::setConfigInternal);
}
return CompletableFuture.completedFuture(this.config);
}
private Config setConfigInternal(final Config config) {
if (Objects.equals(this.config, config))
return config;
this.config = config;
notifyPropertyChanged(BR.config);
return config;
}
public CompletionStage<State> setState(@NonNull final State state) {
if (state != this.state)
return backend.setState(this, state)
.thenApply(this::setStateInternal);
return CompletableFuture.completedFuture(this.state);
}
private State setStateInternal(final State state) {
if (Objects.equals(this.state, state))
return state;
onStateChanged(this.state, state);
this.state = state;
notifyPropertyChanged(BR.state);
return state;
}
private Statistics setStatisticsInternal(final Statistics statistics) {
if (Objects.equals(this.statistics, statistics))
return statistics;
this.statistics = statistics;
notifyPropertyChanged(BR.statistics);
return statistics;
}
public enum State {
DOWN,
TOGGLE,
UNKNOWN,
UP
}
public static class Statistics extends BaseObservable {
}
}