summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/BackendException.java27
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java32
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/Statistics.java13
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java20
-rw-r--r--tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java19
-rw-r--r--tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java148
-rw-r--r--tunnel/src/main/java/com/wireguard/android/util/RootShell.java32
-rw-r--r--tunnel/src/main/java/com/wireguard/android/util/ToolsInstaller.java48
-rw-r--r--tunnel/src/main/java/com/wireguard/config/InetAddresses.java3
-rw-r--r--tunnel/src/main/java/com/wireguard/config/ParseException.java1
-rw-r--r--tunnel/src/main/java/com/wireguard/crypto/Key.java36
-rw-r--r--ui/src/main/java/com/wireguard/android/Application.java25
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.java68
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java16
-rw-r--r--ui/src/main/java/com/wireguard/android/model/ObservableTunnel.java14
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java3
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/VersionPreference.java16
-rw-r--r--ui/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java56
-rw-r--r--ui/src/main/java/com/wireguard/android/util/ErrorMessages.java16
19 files changed, 303 insertions, 290 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/BackendException.java b/tunnel/src/main/java/com/wireguard/android/backend/BackendException.java
index 0f60701b..55fdb602 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/BackendException.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/BackendException.java
@@ -9,6 +9,21 @@ import com.wireguard.util.NonNullForAll;
@NonNullForAll
public final class BackendException extends Exception {
+ private final Object[] format;
+ private final Reason reason;
+ public BackendException(final Reason reason, final Object... format) {
+ this.reason = reason;
+ this.format = format;
+ }
+
+ public Object[] getFormat() {
+ return format;
+ }
+
+ public Reason getReason() {
+ return reason;
+ }
+
public enum Reason {
UNKNOWN_KERNEL_MODULE_NAME,
WG_QUICK_CONFIG_ERROR_CODE,
@@ -18,16 +33,4 @@ public final class BackendException extends Exception {
TUN_CREATION_ERROR,
GO_ACTIVATION_ERROR_CODE
}
- private final Reason reason;
- private final Object[] format;
- public BackendException(final Reason reason, final Object ...format) {
- this.reason = reason;
- this.format = format;
- }
- public Reason getReason() {
- return reason;
- }
- public Object[] getFormat() {
- return format;
- }
}
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
index 54dc913a..70cdd844 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
@@ -34,25 +34,21 @@ import java9.util.concurrent.CompletableFuture;
@NonNullForAll
public final class GoBackend implements Backend {
private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName();
- private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
- public interface AlwaysOnCallback {
- void alwaysOnTriggered();
- }
@Nullable private static AlwaysOnCallback alwaysOnCallback;
- public static void setAlwaysOnCallback(AlwaysOnCallback cb) {
- alwaysOnCallback = cb;
- }
-
+ private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
private final Context context;
- @Nullable private Tunnel currentTunnel;
@Nullable private Config currentConfig;
+ @Nullable private Tunnel currentTunnel;
private int currentTunnelHandle = -1;
-
public GoBackend(final Context context) {
SharedLibraryLoader.loadSharedLibrary(context, "wg-go");
this.context = context;
}
+ public static void setAlwaysOnCallback(AlwaysOnCallback cb) {
+ alwaysOnCallback = cb;
+ }
+
private static native String wgGetConfig(int handle);
private static native int wgGetSocketV4(int handle);
@@ -143,7 +139,7 @@ public final class GoBackend implements Backend {
setStateInternal(currentTunnel, null, State.DOWN);
try {
setStateInternal(tunnel, config, state);
- } catch(final Exception e) {
+ } catch (final Exception e) {
if (originalTunnel != null)
setStateInternal(originalTunnel, originalConfig, State.UP);
throw e;
@@ -209,7 +205,7 @@ public final class GoBackend implements Backend {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
builder.setMetered(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- service.setUnderlyingNetworks(null);
+ service.setUnderlyingNetworks(null);
builder.setBlocking(true);
try (final ParcelFileDescriptor tun = builder.establish()) {
@@ -246,13 +242,13 @@ public final class GoBackend implements Backend {
context.startService(new Intent(context, VpnService.class));
}
+ public interface AlwaysOnCallback {
+ void alwaysOnTriggered();
+ }
+
public static class VpnService extends android.net.VpnService {
@Nullable private GoBackend owner;
- public void setOwner(final GoBackend owner) {
- this.owner = owner;
- }
-
public Builder getBuilder() {
return new Builder();
}
@@ -290,5 +286,9 @@ public final class GoBackend implements Backend {
}
return super.onStartCommand(intent, flags, startId);
}
+
+ public void setOwner(final GoBackend owner) {
+ this.owner = owner;
+ }
}
}
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Statistics.java b/tunnel/src/main/java/com/wireguard/android/backend/Statistics.java
index 54bbe912..b4e01e76 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/Statistics.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/Statistics.java
@@ -16,10 +16,11 @@ import java.util.Map;
@NonNullForAll
public class Statistics {
- private long lastTouched = SystemClock.elapsedRealtime();
private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>();
+ private long lastTouched = SystemClock.elapsedRealtime();
- Statistics() { }
+ Statistics() {
+ }
void add(final Key key, final long rx, final long tx) {
peerBytes.put(key, Pair.create(rx, tx));
@@ -30,10 +31,6 @@ public class Statistics {
return SystemClock.elapsedRealtime() - lastTouched > 900;
}
- public Key[] peers() {
- return peerBytes.keySet().toArray(new Key[0]);
- }
-
public long peerRx(final Key peer) {
if (!peerBytes.containsKey(peer))
return 0;
@@ -46,6 +43,10 @@ public class Statistics {
return peerBytes.get(peer).second;
}
+ public Key[] peers() {
+ return peerBytes.keySet().toArray(new Key[0]);
+ }
+
public long totalRx() {
long rx = 0;
for (final Pair<Long, Long> val : peerBytes.values()) {
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java b/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java
index fccda84f..b9508b1a 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java
@@ -15,16 +15,6 @@ import java.util.regex.Pattern;
@NonNullForAll
public interface Tunnel {
- enum State {
- DOWN,
- TOGGLE,
- UP;
-
- public static State of(final boolean running) {
- return running ? UP : DOWN;
- }
- }
-
int NAME_MAX_LENGTH = 15;
Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}");
@@ -46,4 +36,14 @@ public interface Tunnel {
* @return The new state of the tunnel.
*/
void onStateChange(State newState);
+
+ enum State {
+ DOWN,
+ TOGGLE,
+ UP;
+
+ public static State of(final boolean running) {
+ return running ? UP : DOWN;
+ }
+ }
}
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
index d57a92f9..e731a92c 100644
--- a/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
+++ b/tunnel/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
@@ -42,11 +42,10 @@ import java9.util.stream.Stream;
@NonNullForAll
public final class WgQuickBackend implements Backend {
private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName();
-
- private final RootShell rootShell;
- private final ToolsInstaller toolsInstaller;
private final File localTemporaryDir;
+ private final RootShell rootShell;
private final Map<Tunnel, Config> runningConfigs = new HashMap<>();
+ private final ToolsInstaller toolsInstaller;
private boolean multipleTunnels;
public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) {
@@ -55,10 +54,6 @@ public final class WgQuickBackend implements Backend {
this.toolsInstaller = toolsInstaller;
}
- public void setMultipleTunnels(boolean on) {
- multipleTunnels = on;
- }
-
@Override
public Set<String> getRunningTunnelNames() {
final List<String> output = new ArrayList<>();
@@ -110,6 +105,10 @@ public final class WgQuickBackend implements Backend {
return output.get(0);
}
+ public void setMultipleTunnels(boolean on) {
+ multipleTunnels = on;
+ }
+
@Override
public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception {
final State originalState = getState(tunnel);
@@ -135,7 +134,8 @@ public final class WgQuickBackend implements Backend {
for (final Pair<Tunnel, Config> entry : rewind) {
setStateInternal(entry.first, entry.second, State.UP);
}
- } catch (final Exception ignored) { }
+ } catch (final Exception ignored) {
+ }
throw e;
}
}
@@ -153,7 +153,8 @@ public final class WgQuickBackend implements Backend {
setStateInternal(entry.getKey(), entry.getValue(), State.UP);
}
}
- } catch (final Exception ignored) { }
+ } catch (final Exception ignored) {
+ }
throw e;
}
} else if (state == State.DOWN) {
diff --git a/tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java b/tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java
index 519ad5cf..82e6a096 100644
--- a/tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java
+++ b/tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java
@@ -39,15 +39,14 @@ import androidx.annotation.Nullable;
@NonNullForAll
public class ModuleLoader {
- private static final String MODULE_PUBLIC_KEY_BASE64 = "RWRmHuT9PSqtwfsLtEx+QS06BJtLgFYteL9WCNjH7yuyu5Y1DieSN7If";
private static final String MODULE_LIST_URL = "https://download.wireguard.com/android-module/modules.txt.sig";
- private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s";
private static final String MODULE_NAME = "wireguard-%s.ko";
-
- private final RootShell rootShell;
- private final String userAgent;
+ private static final String MODULE_PUBLIC_KEY_BASE64 = "RWRmHuT9PSqtwfsLtEx+QS06BJtLgFYteL9WCNjH7yuyu5Y1DieSN7If";
+ private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s";
private final File moduleDir;
+ private final RootShell rootShell;
private final File tmpDir;
+ private final String userAgent;
public ModuleLoader(final Context context, final RootShell rootShell, final String userAgent) {
moduleDir = new File(context.getCacheDir(), "kmod");
@@ -56,84 +55,17 @@ public class ModuleLoader {
this.userAgent = userAgent;
}
- public boolean moduleMightExist() {
- return moduleDir.exists() && moduleDir.isDirectory();
- }
-
- public void loadModule() throws IOException, RootShellException {
- rootShell.run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath()));
- }
-
public static boolean isModuleLoaded() {
return new File("/sys/module/wireguard").exists();
}
- private static final class Sha256Digest {
- private byte[] bytes;
- private Sha256Digest(final String hex) {
- if (hex.length() != 64)
- throw new InvalidParameterException("SHA256 hashes must be 32 bytes long");
- bytes = new byte[32];
- for (int i = 0; i < 32; ++i)
- bytes[i] = (byte)Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
- }
- }
-
- @Nullable
- private Map<String, Sha256Digest> verifySignedHashes(final String signifyDigest) {
- final byte[] publicKeyBytes = Base64.decode(MODULE_PUBLIC_KEY_BASE64, Base64.DEFAULT);
-
- if (publicKeyBytes == null || publicKeyBytes.length != 32 + 10 || publicKeyBytes[0] != 'E' || publicKeyBytes[1] != 'd')
- return null;
-
- final String[] lines = signifyDigest.split("\n", 3);
- if (lines.length != 3)
- return null;
- if (!lines[0].startsWith("untrusted comment: "))
- return null;
-
- final byte[] signatureBytes = Base64.decode(lines[1], Base64.DEFAULT);
- if (signatureBytes == null || signatureBytes.length != 64 + 10)
- return null;
- for (int i = 0; i < 10; ++i) {
- if (signatureBytes[i] != publicKeyBytes[i])
- return null;
- }
-
- try {
- EdDSAParameterSpec parameterSpec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
- Signature signature = new EdDSAEngine(MessageDigest.getInstance(parameterSpec.getHashAlgorithm()));
- byte[] rawPublicKeyBytes = new byte[32];
- System.arraycopy(publicKeyBytes, 10, rawPublicKeyBytes, 0, 32);
- signature.initVerify(new EdDSAPublicKey(new EdDSAPublicKeySpec(rawPublicKeyBytes, parameterSpec)));
- signature.update(lines[2].getBytes(StandardCharsets.UTF_8));
- if (!signature.verify(signatureBytes, 10, 64))
- return null;
- } catch (final Exception ignored) {
- return null;
- }
-
- Map<String, Sha256Digest> hashes = new HashMap<>();
- for (final String line : lines[2].split("\n")) {
- final String[] components = line.split(" ", 2);
- if (components.length != 2)
- return null;
- try {
- hashes.put(components[1], new Sha256Digest(components[0]));
- } catch (final Exception ignored) {
- return null;
- }
- }
- return hashes;
- }
-
public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException {
final List<String> output = new ArrayList<>();
rootShell.run(output, "sha256sum /proc/version|cut -d ' ' -f 1");
if (output.size() != 1 || output.get(0).length() != 64)
throw new InvalidParameterException("Invalid sha256 of /proc/version");
final String moduleName = String.format(MODULE_NAME, output.get(0));
- HttpURLConnection connection = (HttpURLConnection)new URL(MODULE_LIST_URL).openConnection();
+ HttpURLConnection connection = (HttpURLConnection) new URL(MODULE_LIST_URL).openConnection();
connection.setRequestProperty("User-Agent", userAgent);
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
@@ -150,7 +82,7 @@ public class ModuleLoader {
throw new InvalidParameterException("The signature did not verify or invalid hash list format");
if (!modules.containsKey(moduleName))
return OsConstants.ENOENT;
- connection = (HttpURLConnection)new URL(String.format(MODULE_URL, moduleName)).openConnection();
+ connection = (HttpURLConnection) new URL(String.format(MODULE_URL, moduleName)).openConnection();
connection.setRequestProperty("User-Agent", userAgent);
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
@@ -185,4 +117,72 @@ public class ModuleLoader {
}
return OsConstants.EXIT_SUCCESS;
}
+
+ public void loadModule() throws IOException, RootShellException {
+ rootShell.run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath()));
+ }
+
+ public boolean moduleMightExist() {
+ return moduleDir.exists() && moduleDir.isDirectory();
+ }
+
+ @Nullable
+ private Map<String, Sha256Digest> verifySignedHashes(final String signifyDigest) {
+ final byte[] publicKeyBytes = Base64.decode(MODULE_PUBLIC_KEY_BASE64, Base64.DEFAULT);
+
+ if (publicKeyBytes == null || publicKeyBytes.length != 32 + 10 || publicKeyBytes[0] != 'E' || publicKeyBytes[1] != 'd')
+ return null;
+
+ final String[] lines = signifyDigest.split("\n", 3);
+ if (lines.length != 3)
+ return null;
+ if (!lines[0].startsWith("untrusted comment: "))
+ return null;
+
+ final byte[] signatureBytes = Base64.decode(lines[1], Base64.DEFAULT);
+ if (signatureBytes == null || signatureBytes.length != 64 + 10)
+ return null;
+ for (int i = 0; i < 10; ++i) {
+ if (signatureBytes[i] != publicKeyBytes[i])
+ return null;
+ }
+
+ try {
+ EdDSAParameterSpec parameterSpec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
+ Signature signature = new EdDSAEngine(MessageDigest.getInstance(parameterSpec.getHashAlgorithm()));
+ byte[] rawPublicKeyBytes = new byte[32];
+ System.arraycopy(publicKeyBytes, 10, rawPublicKeyBytes, 0, 32);
+ signature.initVerify(new EdDSAPublicKey(new EdDSAPublicKeySpec(rawPublicKeyBytes, parameterSpec)));
+ signature.update(lines[2].getBytes(StandardCharsets.UTF_8));
+ if (!signature.verify(signatureBytes, 10, 64))
+ return null;
+ } catch (final Exception ignored) {
+ return null;
+ }
+
+ Map<String, Sha256Digest> hashes = new HashMap<>();
+ for (final String line : lines[2].split("\n")) {
+ final String[] components = line.split(" ", 2);
+ if (components.length != 2)
+ return null;
+ try {
+ hashes.put(components[1], new Sha256Digest(components[0]));
+ } catch (final Exception ignored) {
+ return null;
+ }
+ }
+ return hashes;
+ }
+
+ private static final class Sha256Digest {
+ private byte[] bytes;
+
+ private Sha256Digest(final String hex) {
+ if (hex.length() != 64)
+ throw new InvalidParameterException("SHA256 hashes must be 32 bytes long");
+ bytes = new byte[32];
+ for (int i = 0; i < 32; ++i)
+ bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
+ }
+ }
}
diff --git a/tunnel/src/main/java/com/wireguard/android/util/RootShell.java b/tunnel/src/main/java/com/wireguard/android/util/RootShell.java
index 9f941815..160ba12f 100644
--- a/tunnel/src/main/java/com/wireguard/android/util/RootShell.java
+++ b/tunnel/src/main/java/com/wireguard/android/util/RootShell.java
@@ -187,28 +187,32 @@ public class RootShell {
}
public static class RootShellException extends Exception {
- public enum Reason {
- NO_ROOT_ACCESS,
- SHELL_MARKER_COUNT_ERROR,
- SHELL_EXIT_STATUS_READ_ERROR,
- SHELL_START_ERROR,
- CREATE_BIN_DIR_ERROR,
- CREATE_TEMP_DIR_ERROR
- }
- private final Reason reason;
private final Object[] format;
- public RootShellException(final Reason reason, final Object ...format) {
+ private final Reason reason;
+ public RootShellException(final Reason reason, final Object... format) {
this.reason = reason;
this.format = format;
}
- public boolean isIORelated() {
- return reason != Reason.NO_ROOT_ACCESS;
+
+ public Object[] getFormat() {
+ return format;
}
+
public Reason getReason() {
return reason;
}
- public Object[] getFormat() {
- return format;
+
+ public boolean isIORelated() {
+ return reason != Reason.NO_ROOT_ACCESS;
+ }
+
+ public enum Reason {
+ NO_ROOT_ACCESS,
+ SHELL_MARKER_COUNT_ERROR,
+ SHELL_EXIT_STATUS_READ_ERROR,
+ SHELL_START_ERROR,
+ CREATE_BIN_DIR_ERROR,
+ CREATE_TEMP_DIR_ERROR
}
}
}
diff --git a/tunnel/src/main/java/com/wireguard/android/util/ToolsInstaller.java b/tunnel/src/main/java/com/wireguard/android/util/ToolsInstaller.java
index f12e755f..f3565c1e 100644
--- a/tunnel/src/main/java/com/wireguard/android/util/ToolsInstaller.java
+++ b/tunnel/src/main/java/com/wireguard/android/util/ToolsInstaller.java
@@ -40,9 +40,9 @@ public final class ToolsInstaller {
private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName();
private final Context context;
- private final RootShell rootShell;
private final File localBinaryDir;
private final Object lock = new Object();
+ private final RootShell rootShell;
@Nullable private Boolean areToolsAvailable;
@Nullable private Boolean installAsMagiskModule;
@@ -107,6 +107,29 @@ public final class ToolsInstaller {
}
}
+ public boolean extract() throws IOException {
+ localBinaryDir.mkdirs();
+ final File files[] = new File[EXECUTABLES.length];
+ final File tempFiles[] = new File[EXECUTABLES.length];
+ boolean allExist = true;
+ for (int i = 0; i < files.length; ++i) {
+ files[i] = new File(localBinaryDir, EXECUTABLES[i]);
+ tempFiles[i] = new File(localBinaryDir, EXECUTABLES[i] + ".tmp");
+ allExist &= files[i].exists();
+ }
+ if (allExist)
+ return false;
+ for (int i = 0; i < files.length; ++i) {
+ if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i]))
+ throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
+ if (!tempFiles[i].setExecutable(true, false))
+ throw new IOException("Unable to mark " + tempFiles[i].getAbsolutePath() + " as executable");
+ if (!tempFiles[i].renameTo(files[i]))
+ throw new IOException("Unable to rename " + tempFiles[i].getAbsolutePath() + " to " + files[i].getAbsolutePath());
+ }
+ return true;
+ }
+
public int install() throws RootShellException, IOException {
if (!context.getPackageName().startsWith("com.wireguard."))
throw new SecurityException("The tools may only be installed system-wide from the main WireGuard app.");
@@ -161,29 +184,6 @@ public final class ToolsInstaller {
}
}
- public boolean extract() throws IOException {
- localBinaryDir.mkdirs();
- final File files[] = new File[EXECUTABLES.length];
- final File tempFiles[] = new File[EXECUTABLES.length];
- boolean allExist = true;
- for (int i = 0; i < files.length; ++i) {
- files[i] = new File(localBinaryDir, EXECUTABLES[i]);
- tempFiles[i] = new File(localBinaryDir, EXECUTABLES[i] + ".tmp");
- allExist &= files[i].exists();
- }
- if (allExist)
- return false;
- for (int i = 0; i < files.length; ++i) {
- if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i]))
- throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
- if (!tempFiles[i].setExecutable(true, false))
- throw new IOException("Unable to mark " + tempFiles[i].getAbsolutePath() + " as executable");
- if (!tempFiles[i].renameTo(files[i]))
- throw new IOException("Unable to rename " + tempFiles[i].getAbsolutePath() + " to " + files[i].getAbsolutePath());
- }
- return true;
- }
-
private boolean willInstallAsMagiskModule() {
synchronized (lock) {
if (installAsMagiskModule == null) {
diff --git a/tunnel/src/main/java/com/wireguard/config/InetAddresses.java b/tunnel/src/main/java/com/wireguard/config/InetAddresses.java
index 9b0ab965..573c522d 100644
--- a/tunnel/src/main/java/com/wireguard/config/InetAddresses.java
+++ b/tunnel/src/main/java/com/wireguard/config/InetAddresses.java
@@ -35,7 +35,8 @@ public final class InetAddresses {
PARSER_METHOD = m;
}
- private InetAddresses() { }
+ private InetAddresses() {
+ }
/**
* Parses a numeric IPv4 or IPv6 address without performing any DNS lookups.
diff --git a/tunnel/src/main/java/com/wireguard/config/ParseException.java b/tunnel/src/main/java/com/wireguard/config/ParseException.java
index f4da7ccd..289f1120 100644
--- a/tunnel/src/main/java/com/wireguard/config/ParseException.java
+++ b/tunnel/src/main/java/com/wireguard/config/ParseException.java
@@ -10,6 +10,7 @@ import com.wireguard.util.NonNullForAll;
import androidx.annotation.Nullable;
/**
+ *
*/
@NonNullForAll
public class ParseException extends Exception {
diff --git a/tunnel/src/main/java/com/wireguard/crypto/Key.java b/tunnel/src/main/java/com/wireguard/crypto/Key.java
index fe03fa2d..c11688d5 100644
--- a/tunnel/src/main/java/com/wireguard/crypto/Key.java
+++ b/tunnel/src/main/java/com/wireguard/crypto/Key.java
@@ -204,6 +204,16 @@ public final class Key {
return new Key(publicKey);
}
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this)
+ return true;
+ if (obj == null || obj.getClass() != getClass())
+ return false;
+ final Key other = (Key) obj;
+ return MessageDigest.isEqual(key, other.key);
+ }
+
/**
* Returns the key as an array of bytes.
*
@@ -214,6 +224,14 @@ public final class Key {
return Arrays.copyOf(key, key.length);
}
+ @Override
+ public int hashCode() {
+ int ret = 0;
+ for (int i = 0; i < key.length / 4; ++i)
+ ret ^= (key[i * 4 + 0] >> 0) + (key[i * 4 + 1] >> 8) + (key[i * 4 + 2] >> 16) + (key[i * 4 + 3] >> 24);
+ return ret;
+ }
+
/**
* Encodes the key to base64.
*
@@ -250,24 +268,6 @@ public final class Key {
return new String(output);
}
- @Override
- public int hashCode() {
- int ret = 0;
- for (int i = 0; i < key.length / 4; ++i)
- ret ^= (key[i * 4 + 0] >> 0) + (key[i * 4 + 1] >> 8) + (key[i * 4 + 2] >> 16) + (key[i * 4 + 3] >> 24);
- return ret;
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (obj == this)
- return true;
- if (obj == null || obj.getClass() != getClass())
- return false;
- final Key other = (Key) obj;
- return MessageDigest.isEqual(key, other.key);
- }
-
/**
* The supported formats for encoding a WireGuard key.
*/
diff --git a/ui/src/main/java/com/wireguard/android/Application.java b/ui/src/main/java/com/wireguard/android/Application.java
index 52d5e1ea..6cf4da36 100644
--- a/ui/src/main/java/com/wireguard/android/Application.java
+++ b/ui/src/main/java/com/wireguard/android/Application.java
@@ -37,17 +37,16 @@ import java9.util.concurrent.CompletableFuture;
@NonNullForAll
public class Application extends android.app.Application implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = "WireGuard/" + Application.class.getSimpleName();
public static final String USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, Build.SUPPORTED_ABIS.length > 0 ? Build.SUPPORTED_ABIS[0] : "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT);
-
+ private static final String TAG = "WireGuard/" + Application.class.getSimpleName();
@SuppressWarnings("NullableProblems") private static WeakReference<Application> weakSelf;
private final CompletableFuture<Backend> futureBackend = new CompletableFuture<>();
@SuppressWarnings("NullableProblems") private AsyncWorker asyncWorker;
@Nullable private Backend backend;
+ @SuppressWarnings("NullableProblems") private ModuleLoader moduleLoader;
@SuppressWarnings("NullableProblems") private RootShell rootShell;
@SuppressWarnings("NullableProblems") private SharedPreferences sharedPreferences;
@SuppressWarnings("NullableProblems") private ToolsInstaller toolsInstaller;
- @SuppressWarnings("NullableProblems") private ModuleLoader moduleLoader;
@SuppressWarnings("NullableProblems") private TunnelManager tunnelManager;
public Application() {
@@ -102,6 +101,10 @@ public class Application extends android.app.Application implements SharedPrefer
return get().futureBackend;
}
+ public static ModuleLoader getModuleLoader() {
+ return get().moduleLoader;
+ }
+
public static RootShell getRootShell() {
return get().rootShell;
}
@@ -114,10 +117,6 @@ public class Application extends android.app.Application implements SharedPrefer
return get().toolsInstaller;
}
- public static ModuleLoader getModuleLoader() {
- return get().moduleLoader;
- }
-
public static TunnelManager getTunnelManager() {
return get().tunnelManager;
}
@@ -168,14 +167,14 @@ public class Application extends android.app.Application implements SharedPrefer
}
@Override
- public void onTerminate() {
- sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
- super.onTerminate();
+ public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
+ if ("multiple_tunnels".equals(key) && backend != null && backend instanceof WgQuickBackend)
+ ((WgQuickBackend) backend).setMultipleTunnels(sharedPreferences.getBoolean(key, false));
}
@Override
- public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
- if ("multiple_tunnels".equals(key) && backend != null && backend instanceof WgQuickBackend)
- ((WgQuickBackend)backend).setMultipleTunnels(sharedPreferences.getBoolean(key, false));
+ public void onTerminate() {
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
+ super.onTerminate();
}
}
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.java b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.java
index 762efc5d..e2adecc3 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.java
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.java
@@ -34,8 +34,20 @@ import androidx.databinding.DataBindingUtil;
@NonNullForAll
public class TunnelDetailFragment extends BaseFragment {
@Nullable private TunnelDetailFragmentBinding binding;
- @Nullable private Timer timer;
@Nullable private State lastState = State.TOGGLE;
+ @Nullable private Timer timer;
+
+ private String formatBytes(final long bytes) {
+ if (bytes < 1024)
+ return requireContext().getString(R.string.transfer_bytes, bytes);
+ else if (bytes < 1024 * 1024)
+ return requireContext().getString(R.string.transfer_kibibytes, bytes / 1024.0);
+ else if (bytes < 1024 * 1024 * 1024)
+ return requireContext().getString(R.string.transfer_mibibytes, bytes / (1024.0 * 1024.0));
+ else if (bytes < 1024 * 1024 * 1024 * 1024)
+ return requireContext().getString(R.string.transfer_gibibytes, bytes / (1024.0 * 1024.0 * 1024.0));
+ return requireContext().getString(R.string.transfer_tibibytes, bytes / (1024.0 * 1024.0 * 1024.0) / 1024.0);
+ }
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@@ -49,27 +61,6 @@ public class TunnelDetailFragment extends BaseFragment {
}
@Override
- public void onStop() {
- super.onStop();
- if (timer != null) {
- timer.cancel();
- timer = null;
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- timer = new Timer();
- timer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- updateStats();
- }
- }, 0, 1000);
- }
-
- @Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
@@ -87,6 +78,18 @@ public class TunnelDetailFragment extends BaseFragment {
}
@Override
+ public void onResume() {
+ super.onResume();
+ timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ updateStats();
+ }
+ }, 0, 1000);
+ }
+
+ @Override
public void onSelectedTunnelChanged(@Nullable final ObservableTunnel oldTunnel, @Nullable final ObservableTunnel newTunnel) {
if (binding == null)
return;
@@ -100,6 +103,15 @@ public class TunnelDetailFragment extends BaseFragment {
}
@Override
+ public void onStop() {
+ super.onStop();
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ }
+ }
+
+ @Override
public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
if (binding == null) {
return;
@@ -110,18 +122,6 @@ public class TunnelDetailFragment extends BaseFragment {
super.onViewStateRestored(savedInstanceState);
}
- private String formatBytes(final long bytes) {
- if (bytes < 1024)
- return requireContext().getString(R.string.transfer_bytes, bytes);
- else if (bytes < 1024*1024)
- return requireContext().getString(R.string.transfer_kibibytes, bytes/1024.0);
- else if (bytes < 1024*1024*1024)
- return requireContext().getString(R.string.transfer_mibibytes, bytes/(1024.0*1024.0));
- else if (bytes < 1024*1024*1024*1024)
- return requireContext().getString(R.string.transfer_gibibytes, bytes/(1024.0*1024.0*1024.0));
- return requireContext().getString(R.string.transfer_tibibytes, bytes/(1024.0*1024.0*1024.0)/1024.0);
- }
-
private void updateStats() {
if (binding == null || !isResumed())
return;
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
index 995a2d59..7ed82f1f 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
@@ -262,14 +262,6 @@ public class TunnelListFragment extends BaseFragment {
});
}
- private void showSnackbar(final CharSequence message) {
- if (binding != null) {
- final Snackbar snackbar = Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG);
- snackbar.setAnchorView(binding.createFab);
- snackbar.show();
- }
- }
-
private void onTunnelDeletionFinished(final Integer count, @Nullable final Throwable throwable) {
final String message;
if (throwable == null) {
@@ -337,6 +329,14 @@ public class TunnelListFragment extends BaseFragment {
});
}
+ private void showSnackbar(final CharSequence message) {
+ if (binding != null) {
+ final Snackbar snackbar = Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG);
+ snackbar.setAnchorView(binding.createFab);
+ snackbar.show();
+ }
+ }
+
private MultiselectableRelativeLayout viewForTunnel(final ObservableTunnel tunnel, final List tunnels) {
return (MultiselectableRelativeLayout) binding.tunnelList.findViewHolderForAdapterPosition(tunnels.indexOf(tunnel)).itemView;
}
diff --git a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.java b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.java
index 3a9fa229..b0f8387b 100644
--- a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.java
+++ b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.java
@@ -27,12 +27,12 @@ import java9.util.concurrent.CompletionStage;
public class ObservableTunnel extends BaseObservable implements Keyed<String>, Tunnel {
private final TunnelManager manager;
@Nullable private Config config;
- private State state;
private String name;
+ private State state;
@Nullable private Statistics statistics;
ObservableTunnel(final TunnelManager manager, final String name,
- @Nullable final Config config, final State state) {
+ @Nullable final Config config, final State state) {
this.name = name;
this.manager = manager;
this.config = config;
@@ -103,6 +103,11 @@ public class ObservableTunnel extends BaseObservable implements Keyed<String>, T
return name;
}
+ @Override
+ public void onStateChange(final State newState) {
+ onStateChanged(newState);
+ }
+
State onStateChanged(final State state) {
if (state != State.UP)
onStatisticsChanged(null);
@@ -111,11 +116,6 @@ public class ObservableTunnel extends BaseObservable implements Keyed<String>, T
return state;
}
- @Override
- public void onStateChange(final State newState) {
- onStateChanged(newState);
- }
-
@Nullable
Statistics onStatisticsChanged(@Nullable final Statistics statistics) {
this.statistics = statistics;
diff --git a/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java b/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
index 43494aaa..32b3e33c 100644
--- a/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
+++ b/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
@@ -47,8 +47,7 @@ public class LogExporterPreference extends Preference {
final Process process = Runtime.getRuntime().exec(new String[]{
"logcat", "-b", "all", "-d", "-v", "threadtime", "*:V"});
try (final BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
- final BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())))
- {
+ final BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = stdout.readLine()) != null) {
outputFile.getOutputStream().write(line.getBytes());
diff --git a/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java b/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java
index b07df3e5..47266fa8 100644
--- a/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java
+++ b/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java
@@ -28,14 +28,6 @@ import androidx.preference.Preference;
public class VersionPreference extends Preference {
@Nullable private String versionSummary;
- private String getBackendPrettyName(final Context context, final Backend backend) {
- if (backend instanceof GoBackend)
- return context.getString(R.string.type_name_kernel_module);
- if (backend instanceof WgQuickBackend)
- return context.getString(R.string.type_name_go_userspace);
- return "";
- }
-
public VersionPreference(final Context context, final AttributeSet attrs) {
super(context, attrs);
@@ -50,6 +42,14 @@ public class VersionPreference extends Preference {
});
}
+ private String getBackendPrettyName(final Context context, final Backend backend) {
+ if (backend instanceof GoBackend)
+ return context.getString(R.string.type_name_kernel_module);
+ if (backend instanceof WgQuickBackend)
+ return context.getString(R.string.type_name_go_userspace);
+ return "";
+ }
+
@Nullable
@Override
public CharSequence getSummary() {
diff --git a/ui/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java b/ui/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java
index b4772ef4..c1c667f5 100644
--- a/ui/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java
+++ b/ui/src/main/java/com/wireguard/android/util/DownloadsFileSaver.java
@@ -26,30 +26,6 @@ import java.io.OutputStream;
@NonNullForAll
public class DownloadsFileSaver {
- public static class DownloadsFile {
- private Context context;
- private OutputStream outputStream;
- private String fileName;
- private Uri uri;
-
- private DownloadsFile(final Context context, final OutputStream outputStream, final String fileName, final Uri uri) {
- this.context = context;
- this.outputStream = outputStream;
- this.fileName = fileName;
- this.uri = uri;
- }
-
- public OutputStream getOutputStream() { return outputStream; }
- public String getFileName() { return fileName; }
-
- public void delete() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- context.getContentResolver().delete(uri, null, null);
- else
- new File(fileName).delete();
- }
- }
-
public static DownloadsFile save(final Context context, final String name, final String mimeType, final boolean overwriteExisting) throws Exception {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
final ContentResolver contentResolver = context.getContentResolver();
@@ -89,12 +65,40 @@ public class DownloadsFileSaver {
}
return new DownloadsFile(context, contentStream, path, contentUri);
} else {
- @SuppressWarnings("deprecation")
- final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+ @SuppressWarnings("deprecation") final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
final File file = new File(path, name);
if (!path.isDirectory() && !path.mkdirs())
throw new IOException(context.getString(R.string.create_output_dir_error));
return new DownloadsFile(context, new FileOutputStream(file), file.getAbsolutePath(), null);
}
}
+
+ public static class DownloadsFile {
+ private Context context;
+ private String fileName;
+ private OutputStream outputStream;
+ private Uri uri;
+
+ private DownloadsFile(final Context context, final OutputStream outputStream, final String fileName, final Uri uri) {
+ this.context = context;
+ this.outputStream = outputStream;
+ this.fileName = fileName;
+ this.uri = uri;
+ }
+
+ public void delete() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+ context.getContentResolver().delete(uri, null, null);
+ else
+ new File(fileName).delete();
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+ }
}
diff --git a/ui/src/main/java/com/wireguard/android/util/ErrorMessages.java b/ui/src/main/java/com/wireguard/android/util/ErrorMessages.java
index accc7ef7..e266cfed 100644
--- a/ui/src/main/java/com/wireguard/android/util/ErrorMessages.java
+++ b/ui/src/main/java/com/wireguard/android/util/ErrorMessages.java
@@ -51,14 +51,6 @@ public final class ErrorMessages {
BackendException.Reason.TUN_CREATION_ERROR, R.string.tun_create_error,
BackendException.Reason.GO_ACTIVATION_ERROR_CODE, R.string.tunnel_on_error
));
- private static final Map<RootShellException.Reason, Integer> RSE_REASON_MAP = new EnumMap<>(Maps.of(
- RootShellException.Reason.NO_ROOT_ACCESS, R.string.error_root,
- RootShellException.Reason.SHELL_MARKER_COUNT_ERROR, R.string.shell_marker_count_error,
- RootShellException.Reason.SHELL_EXIT_STATUS_READ_ERROR, R.string.shell_exit_status_read_error,
- RootShellException.Reason.SHELL_START_ERROR, R.string.shell_start_error,
- RootShellException.Reason.CREATE_BIN_DIR_ERROR, R.string.create_bin_dir_error,
- RootShellException.Reason.CREATE_TEMP_DIR_ERROR, R.string.create_temp_dir_error
- ));
private static final Map<Format, Integer> KFE_FORMAT_MAP = new EnumMap<>(Maps.of(
Format.BASE64, R.string.key_length_explanation_base64,
Format.BINARY, R.string.key_length_explanation_binary,
@@ -74,6 +66,14 @@ public final class ErrorMessages {
InetNetwork.class, R.string.parse_error_inet_network,
Integer.class, R.string.parse_error_integer
);
+ private static final Map<RootShellException.Reason, Integer> RSE_REASON_MAP = new EnumMap<>(Maps.of(
+ RootShellException.Reason.NO_ROOT_ACCESS, R.string.error_root,
+ RootShellException.Reason.SHELL_MARKER_COUNT_ERROR, R.string.shell_marker_count_error,
+ RootShellException.Reason.SHELL_EXIT_STATUS_READ_ERROR, R.string.shell_exit_status_read_error,
+ RootShellException.Reason.SHELL_START_ERROR, R.string.shell_start_error,
+ RootShellException.Reason.CREATE_BIN_DIR_ERROR, R.string.create_bin_dir_error,
+ RootShellException.Reason.CREATE_TEMP_DIR_ERROR, R.string.create_temp_dir_error
+ ));
private ErrorMessages() {
// Prevent instantiation