Serviceless rewrite, part 1
Signed-off-by: Samuel Holland <samuel@sholland.org>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package com.wireguard.android.configStore;
|
||||
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import java9.util.concurrent.CompletionStage;
|
||||
|
||||
/**
|
||||
* Interface for persistent storage providers for WireGuard configurations.
|
||||
*/
|
||||
|
||||
public interface ConfigStore {
|
||||
/**
|
||||
* Create a persistent tunnel, which must have a unique name within the persistent storage
|
||||
* medium.
|
||||
*
|
||||
* @param name The name of the tunnel to create.
|
||||
* @param config Configuration for the new tunnel.
|
||||
* @return A future completed when the tunnel and its configuration have been saved to
|
||||
* persistent storage. This future encapsulates the configuration that was actually saved to
|
||||
* persistent storage. This future will always be completed on the main thread.
|
||||
*/
|
||||
CompletionStage<Config> create(final String name, final Config config);
|
||||
|
||||
/**
|
||||
* Delete a persistent tunnel.
|
||||
*
|
||||
* @param name The name of the tunnel to delete.
|
||||
* @return A future completed when the tunnel and its configuration have been deleted. This
|
||||
* future will always be completed on the main thread.
|
||||
*/
|
||||
CompletionStage<Void> delete(final String name);
|
||||
|
||||
/**
|
||||
* Enumerate the names of tunnels present in persistent storage.
|
||||
*
|
||||
* @return A future completed when the set of present tunnel names is available. This future
|
||||
* will always be completed on the main thread.
|
||||
*/
|
||||
CompletionStage<Set<String>> enumerate();
|
||||
|
||||
/**
|
||||
* Load the configuration for the tunnel given by {@code name}.
|
||||
*
|
||||
* @param name The identifier for the configuration in persistent storage (i.e. the name of the
|
||||
* tunnel).
|
||||
* @return A future completed when an in-memory representation of the configuration is
|
||||
* available. This future encapsulates the configuration loaded from persistent storage. This
|
||||
* future will always be completed on the main thread.
|
||||
*/
|
||||
CompletionStage<Config> load(final String name);
|
||||
|
||||
/**
|
||||
* Save the configuration for an existing tunnel given by {@code name}.
|
||||
*
|
||||
* @param name The identifier for the configuration in persistent storage (i.e. the name of
|
||||
* the tunnel).
|
||||
* @param config An updated configuration object for the tunnel.
|
||||
* @return A future completed when the configuration has been saved to persistent storage. This
|
||||
* future encapsulates the configuration that was actually saved to persistent storage. This
|
||||
* future will always be completed on the main thread.
|
||||
*/
|
||||
CompletionStage<Config> save(final String name, final Config config);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.wireguard.android.configStore;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wireguard.android.Application.ApplicationContext;
|
||||
import com.wireguard.android.util.AsyncWorker;
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
|
||||
import java9.util.concurrent.CompletionStage;
|
||||
import java9.util.stream.Collectors;
|
||||
import java9.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Created by samuel on 12/28/17.
|
||||
*/
|
||||
|
||||
public final class FileConfigStore implements ConfigStore {
|
||||
private static final String TAG = FileConfigStore.class.getSimpleName();
|
||||
|
||||
private final AsyncWorker asyncWorker;
|
||||
private final Context context;
|
||||
|
||||
public FileConfigStore(final AsyncWorker asyncWorker,
|
||||
@ApplicationContext final Context context) {
|
||||
this.asyncWorker = asyncWorker;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<Config> create(final String name, final Config config) {
|
||||
return asyncWorker.supplyAsync(() -> {
|
||||
final File file = fileFor(name);
|
||||
if (!file.createNewFile()) {
|
||||
final String message = "Configuration file " + file.getName() + " already exists";
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
try (FileOutputStream stream = new FileOutputStream(file, false)) {
|
||||
stream.write(config.toString().getBytes(StandardCharsets.UTF_8));
|
||||
return config;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<Void> delete(final String name) {
|
||||
return asyncWorker.runAsync(() -> {
|
||||
final File file = fileFor(name);
|
||||
if (!file.delete())
|
||||
throw new IOException("Cannot delete configuration file " + file.getName());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<Set<String>> enumerate() {
|
||||
return asyncWorker.supplyAsync(() -> Stream.of(context.fileList())
|
||||
.filter(name -> name.endsWith(".conf"))
|
||||
.map(name -> name.substring(0, name.length() - ".conf".length()))
|
||||
.collect(Collectors.toUnmodifiableSet()));
|
||||
}
|
||||
|
||||
private File fileFor(final String name) {
|
||||
return new File(context.getFilesDir(), name + ".conf");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<Config> load(final String name) {
|
||||
return asyncWorker.supplyAsync(() -> {
|
||||
try (FileInputStream stream = new FileInputStream(fileFor(name))) {
|
||||
return Config.from(stream);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<Config> save(final String name, final Config config) {
|
||||
Log.d(TAG, "Requested save config for tunnel " + name);
|
||||
return asyncWorker.supplyAsync(() -> {
|
||||
final File file = fileFor(name);
|
||||
if (!file.isFile()) {
|
||||
final String message = "Configuration file " + file.getName() + " not found";
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
try (FileOutputStream stream = new FileOutputStream(file, false)) {
|
||||
Log.d(TAG, "Writing out config for tunnel " + name);
|
||||
stream.write(config.toString().getBytes(StandardCharsets.UTF_8));
|
||||
return config;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user