diff options
6 files changed, 174 insertions, 1 deletions
diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Backend.java b/tunnel/src/main/java/com/wireguard/android/backend/Backend.java index 4c18d98b..c67d672b 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/Backend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/Backend.java @@ -30,6 +30,7 @@ public interface Backend { * * @param tunnel The tunnel to examine the state of. * @return The state of the tunnel. + * @throws Exception Exception raised when retrieving tunnel's state. */ Tunnel.State getState(Tunnel tunnel) throws Exception; @@ -39,6 +40,7 @@ public interface Backend { * * @param tunnel The tunnel to retrieve statistics for. * @return The statistics for the tunnel. + * @throws Exception Exception raised when retrieving statistics. */ Statistics getStatistics(Tunnel tunnel) throws Exception; @@ -46,7 +48,7 @@ public interface Backend { * Determine version of underlying backend. * * @return The version of the backend. - * @throws Exception + * @throws Exception Exception raised while retrieving version. */ String getVersion() throws Exception; @@ -59,6 +61,7 @@ public interface Backend { * {@code TOGGLE}. * @param config The configuration for this tunnel, may be null if state is {@code DOWN}. * @return The updated state of the tunnel. + * @throws Exception Exception raised while changing state. */ Tunnel.State setState(Tunnel tunnel, Tunnel.State state, @Nullable Config config) throws Exception; } 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 5552b3ee..b64ebb47 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/BackendException.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/BackendException.java @@ -7,24 +7,47 @@ package com.wireguard.android.backend; import com.wireguard.util.NonNullForAll; +/** + * A subclass of {@link Exception} that encapsulates the reasons for a failure originating in + * implementations of {@link Backend}. + */ @NonNullForAll public final class BackendException extends Exception { private final Object[] format; private final Reason reason; + /** + * Public constructor for BackendException. + * + * @param reason The {@link Reason} which caused this exception to be thrown + * @param format Format string values used when converting exceptions to user-facing strings. + */ public BackendException(final Reason reason, final Object... format) { this.reason = reason; this.format = format; } + /** + * Get the format string values associated with the instance. + * + * @return Array of {@link Object} for string formatting purposes + */ public Object[] getFormat() { return format; } + /** + * Get the reason for this exception. + * + * @return Associated {@link Reason} for this exception. + */ public Reason getReason() { return reason; } + /** + * Enum class containing all known reasons for why a {@link BackendException} might be thrown. + */ public enum Reason { UNKNOWN_KERNEL_MODULE_NAME, WG_QUICK_CONFIG_ERROR_CODE, 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 9fafc918..5c3bf111 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -34,6 +34,10 @@ import java.util.concurrent.TimeoutException; import androidx.annotation.Nullable; import androidx.collection.ArraySet; +/** + * Implementation of {@link Backend} that uses the wireguard-go userspace implementation to provide + * WireGuard tunnels. + */ @NonNullForAll public final class GoBackend implements Backend { private static final String TAG = "WireGuard/GoBackend"; @@ -44,11 +48,22 @@ public final class GoBackend implements Backend { @Nullable private Tunnel currentTunnel; private int currentTunnelHandle = -1; + /** + * Public constructor for GoBackend. + * + * @param context An Android {@link Context} + */ public GoBackend(final Context context) { SharedLibraryLoader.loadSharedLibrary(context, "wg-go"); this.context = context; } + /** + * Set a {@link AlwaysOnCallback} to be invoked when {@link VpnService} is started by the + * system's Always-On VPN mode. + * + * @param cb Callback to be invoked + */ public static void setAlwaysOnCallback(final AlwaysOnCallback cb) { alwaysOnCallback = cb; } @@ -65,6 +80,11 @@ public final class GoBackend implements Backend { private static native String wgVersion(); + /** + * Method to get the names of running tunnels. + * + * @return A set of string values denoting names of running tunnels. + */ @Override public Set<String> getRunningTunnelNames() { if (currentTunnel != null) { @@ -75,11 +95,23 @@ public final class GoBackend implements Backend { return Collections.emptySet(); } + /** + * Get the associated {@link State} for a given {@link Tunnel}. + * + * @param tunnel The tunnel to examine the state of. + * @return {@link State} associated with the given tunnel. + */ @Override public State getState(final Tunnel tunnel) { return currentTunnel == tunnel ? State.UP : State.DOWN; } + /** + * Get the associated {@link Statistics} for a given {@link Tunnel}. + * + * @param tunnel The tunnel to retrieve statistics for. + * @return {@link Statistics} associated with the given tunnel. + */ @Override public Statistics getStatistics(final Tunnel tunnel) { final Statistics stats = new Statistics(); @@ -124,11 +156,26 @@ public final class GoBackend implements Backend { return stats; } + /** + * Get the version of the underlying wireguard-go library. + * + * @return {@link String} value of the version of the wireguard-go library. + */ @Override public String getVersion() { return wgVersion(); } + /** + * Change the state of a given {@link Tunnel}, optionally applying a given {@link Config}. + * + * @param tunnel The tunnel to control the state of. + * @param state The new state for this tunnel. Must be {@code UP}, {@code DOWN}, or + * {@code TOGGLE}. + * @param config The configuration for this tunnel, may be null if state is {@code DOWN}. + * @return {@link State} of the tunnel after state changes are applied. + * @throws Exception Exception raised while changing tunnel state. + */ @Override public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception { final State originalState = getState(tunnel); @@ -260,6 +307,10 @@ public final class GoBackend implements Backend { context.startService(new Intent(context, VpnService.class)); } + /** + * Callback for {@link GoBackend} that is invoked when {@link VpnService} is started by the + * system's Always-On VPN mode. + */ public interface AlwaysOnCallback { void alwaysOnTriggered(); } @@ -293,6 +344,9 @@ public final class GoBackend implements Backend { } } + /** + * {@link android.net.VpnService} implementation for {@link GoBackend} + */ public static class VpnService extends android.net.VpnService { @Nullable private GoBackend 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 b4e01e76..60f78833 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/Statistics.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/Statistics.java @@ -14,6 +14,9 @@ import com.wireguard.util.NonNullForAll; import java.util.HashMap; import java.util.Map; +/** + * Class representing transfer statistics for a {@link Tunnel} instance. + */ @NonNullForAll public class Statistics { private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>(); @@ -22,31 +25,70 @@ public class Statistics { Statistics() { } + /** + * Add a peer and its current data usage to the internal map. + * + * @param key A WireGuard public key bound to a particular peer + * @param rx The received traffic for the {@link com.wireguard.config.Peer} referenced by + * the provided {@link Key}. This value is in bytes + * @param tx The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by + * the provided {@link Key}. This value is in bytes. + */ void add(final Key key, final long rx, final long tx) { peerBytes.put(key, Pair.create(rx, tx)); lastTouched = SystemClock.elapsedRealtime(); } + /** + * Check if the statistics are stale, indicating the need for the {@link Backend} to update them. + * + * @return boolean indicating if the current statistics instance has stale values. + */ public boolean isStale() { return SystemClock.elapsedRealtime() - lastTouched > 900; } + /** + * Get the received traffic (in bytes) for the {@link com.wireguard.config.Peer} referenced by + * the provided {@link Key} + * + * @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}. + * @return a long representing the number of bytes received by this peer. + */ public long peerRx(final Key peer) { if (!peerBytes.containsKey(peer)) return 0; return peerBytes.get(peer).first; } + /** + * Get the transmitted traffic (in bytes) for the {@link com.wireguard.config.Peer} referenced by + * the provided {@link Key} + * + * @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}. + * @return a long representing the number of bytes transmitted by this peer. + */ public long peerTx(final Key peer) { if (!peerBytes.containsKey(peer)) return 0; return peerBytes.get(peer).second; } + /** + * Get the list of peers being tracked by this instance. + * + * @return An array of {@link Key} instances representing WireGuard + * {@link com.wireguard.config.Peer}s + */ public Key[] peers() { return peerBytes.keySet().toArray(new Key[0]); } + /** + * Get the total received traffic by all the peers being tracked by this instance + * + * @return a long representing the number of bytes received by the peers being tracked. + */ public long totalRx() { long rx = 0; for (final Pair<Long, Long> val : peerBytes.values()) { @@ -55,6 +97,11 @@ public class Statistics { return rx; } + /** + * Get the total transmitted traffic by all the peers being tracked by this instance + * + * @return a long representing the number of bytes transmitted by the peers being tracked. + */ public long totalTx() { long tx = 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 a9761929..2f12f6c3 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/Tunnel.java @@ -36,11 +36,20 @@ public interface Tunnel { */ void onStateChange(State newState); + /** + * Enum class to represent all possible states of a {@link Tunnel}. + */ enum State { DOWN, TOGGLE, UP; + /** + * Get the state of a {@link Tunnel} + * + * @param running boolean indicating if the tunnel is running. + * @return State of the tunnel based on whether or not it is running. + */ public static State of(final boolean running) { return running ? UP : 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 c5b0aff5..e6605057 100644 --- a/tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java +++ b/tunnel/src/main/java/com/wireguard/android/util/ModuleLoader.java @@ -31,6 +31,10 @@ import java.util.Map; import androidx.annotation.Nullable; +/** + * Class that implements the logic for downloading and loading signed, prebuilt modules for + * WireGuard into the running kernel. + */ @NonNullForAll @SuppressWarnings("MagicNumber") public class ModuleLoader { @@ -43,6 +47,15 @@ public class ModuleLoader { private final File tmpDir; private final String userAgent; + /** + * Public constructor for ModuleLoader + * + * @param context A {@link Context} instance. + * @param rootShell A {@link RootShell} instance used to run elevated commands required for module + * loading. + * @param userAgent A {@link String} that represents the User-Agent string used for connections + * to the upstream server. + */ public ModuleLoader(final Context context, final RootShell rootShell, final String userAgent) { moduleDir = new File(context.getCacheDir(), "kmod"); tmpDir = new File(context.getCacheDir(), "tmp"); @@ -50,10 +63,23 @@ public class ModuleLoader { this.userAgent = userAgent; } + /** + * Check whether a WireGuard module is already loaded into the kernel. + * + * @return boolean indicating if WireGuard is already enabled in the kernel. + */ public static boolean isModuleLoaded() { return new File("/sys/module/wireguard").exists(); } + /** + * Download the correct WireGuard module for the device + * + * @return {@link OsConstants}.EXIT_SUCCESS if everything succeeds, ENOENT otherwise. + * @throws IOException if the remote hash list was not found or empty. + * @throws RootShellException if {@link RootShell} has a failure executing elevated commands. + * @throws NoSuchAlgorithmException if SHA256 algorithm is not available in device JDK. + */ public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException { final List<String> output = new ArrayList<>(); rootShell.run(output, "sha256sum /proc/version|cut -d ' ' -f 1"); @@ -113,10 +139,21 @@ public class ModuleLoader { return OsConstants.EXIT_SUCCESS; } + /** + * Load the downloaded module. ModuleLoader#download must be called before this. + * + * @throws IOException if {@link RootShell} has a failure executing elevated commands. + * @throws RootShellException if {@link RootShell} has a failure executing elevated commands. + */ public void loadModule() throws IOException, RootShellException { rootShell.run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath())); } + /** + * Check if the module might already exist in the app's data. + * + * @return boolean indicating whether downloadable module might exist already. + */ public boolean moduleMightExist() { return moduleDir.exists() && moduleDir.isDirectory(); } |