mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3de549d2c7 | |||
| 408e9004b0 | |||
| e1a66d5766 | |||
| 99cf2152c4 | |||
| 53e8d425e9 | |||
| 9e5f45da15 | |||
| 4c0caa10e9 | |||
| b9991e4229 | |||
| 4acee49d4b | |||
| d3a8291a7a | |||
| bcae77b989 | |||
| de75ff4bea | |||
| b10a6171a5 | |||
| 6534f45a3a | |||
| 373145d30e | |||
| 5feea74f28 | |||
| e8891d775b | |||
| 0f128f99a1 | |||
| 61e3441bfb | |||
| 15e10d8fde |
Generated
-1
@@ -150,7 +150,6 @@
|
||||
<inspection_tool class="FieldMayBeFinal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FieldMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FieldMustBeInitialized" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="FieldNotUsedInToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FinalMethodInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="ForLoopReplaceableByWhile" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
|
||||
+6
-7
@@ -5,21 +5,19 @@ apply plugin: 'com.android.application'
|
||||
final def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
|
||||
android {
|
||||
buildToolsVersion '27.0.3'
|
||||
buildToolsVersion '28.0.0'
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
compileSdkVersion 27
|
||||
dataBinding {
|
||||
enabled true
|
||||
}
|
||||
dataBinding.enabled true
|
||||
defaultConfig {
|
||||
applicationId 'com.wireguard.android'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 27
|
||||
versionCode 423
|
||||
versionName '0.0.20180606'
|
||||
versionCode 427
|
||||
versionName '0.0.20180625'
|
||||
}
|
||||
// If the keystore file exists
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
@@ -49,8 +47,9 @@ android {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
databindingVersion = '3.1.2'
|
||||
databindingVersion = '3.1.3'
|
||||
supportLibsVersion = '27.1.1'
|
||||
streamsupportVersion = '1.6.0'
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.wireguard.android.util.ToolsInstaller;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class Application extends android.app.Application {
|
||||
@@ -34,6 +36,10 @@ public class Application extends android.app.Application {
|
||||
private SharedPreferences sharedPreferences;
|
||||
private ToolsInstaller toolsInstaller;
|
||||
private TunnelManager tunnelManager;
|
||||
private Handler handler;
|
||||
private List<BackendCallback> haveBackendCallbacks = new ArrayList<>();
|
||||
private final Object haveBackendCallbacksLock = new Object();
|
||||
|
||||
public Application() {
|
||||
weakSelf = new WeakReference<>(this);
|
||||
}
|
||||
@@ -47,11 +53,40 @@ public class Application extends android.app.Application {
|
||||
}
|
||||
|
||||
public static Backend getBackend() {
|
||||
return get().backend;
|
||||
final Application app = get();
|
||||
synchronized (app) {
|
||||
if (app.backend == null) {
|
||||
if (new File("/sys/module/wireguard").exists()) {
|
||||
try {
|
||||
app.rootShell.start();
|
||||
app.backend = new WgQuickBackend(app.getApplicationContext());
|
||||
} catch (final Exception ignored) { }
|
||||
}
|
||||
if (app.backend == null)
|
||||
app.backend = new GoBackend(app.getApplicationContext());
|
||||
synchronized (app.haveBackendCallbacksLock) {
|
||||
for (final BackendCallback callback : app.haveBackendCallbacks)
|
||||
app.handler.post(() -> callback.callback(app.backend));
|
||||
app.haveBackendCallbacks = null;
|
||||
}
|
||||
}
|
||||
return app.backend;
|
||||
}
|
||||
}
|
||||
|
||||
public static Class getBackendType() {
|
||||
return get().backend.getClass();
|
||||
@FunctionalInterface
|
||||
public interface BackendCallback {
|
||||
void callback(final Backend backend);
|
||||
}
|
||||
|
||||
public static void onHaveBackend(final BackendCallback callback) {
|
||||
final Application app = get();
|
||||
synchronized (app.haveBackendCallbacksLock) {
|
||||
if (app.haveBackendCallbacks == null)
|
||||
callback.callback(app.backend);
|
||||
else
|
||||
app.haveBackendCallbacks.add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public static RootShell getRootShell() {
|
||||
@@ -74,8 +109,8 @@ public class Application extends android.app.Application {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
final Executor executor = AsyncTask.SERIAL_EXECUTOR;
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
final ConfigStore configStore = new FileConfigStore(getApplicationContext());
|
||||
|
||||
asyncWorker = new AsyncWorker(executor, handler);
|
||||
@@ -87,12 +122,8 @@ public class Application extends android.app.Application {
|
||||
sharedPreferences.getBoolean("dark_theme", false) ?
|
||||
AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
|
||||
|
||||
if (new File("/sys/module/wireguard").exists())
|
||||
backend = new WgQuickBackend(getApplicationContext());
|
||||
else
|
||||
backend = new GoBackend(getApplicationContext());
|
||||
|
||||
tunnelManager = new TunnelManager(backend, configStore);
|
||||
tunnelManager = new TunnelManager(configStore);
|
||||
asyncWorker.runAsync(Application::getBackend);
|
||||
tunnelManager.onCreate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,18 +20,20 @@ public class BootShutdownReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
if (Application.getBackendType() != WgQuickBackend.class)
|
||||
return;
|
||||
final String action = intent.getAction();
|
||||
if (action == null)
|
||||
return;
|
||||
final TunnelManager tunnelManager = Application.getTunnelManager();
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
|
||||
Log.i(TAG, "Broadcast receiver restoring state (boot)");
|
||||
tunnelManager.restoreState(false).whenComplete(ExceptionLoggers.D);
|
||||
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
|
||||
Log.i(TAG, "Broadcast receiver saving state (shutdown)");
|
||||
tunnelManager.saveState();
|
||||
}
|
||||
Application.onHaveBackend(backend -> {
|
||||
if (backend.getClass() != WgQuickBackend.class)
|
||||
return;
|
||||
final String action = intent.getAction();
|
||||
if (action == null)
|
||||
return;
|
||||
final TunnelManager tunnelManager = Application.getTunnelManager();
|
||||
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
|
||||
Log.i(TAG, "Broadcast receiver restoring state (boot)");
|
||||
tunnelManager.restoreState(false).whenComplete(ExceptionLoggers.D);
|
||||
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
|
||||
Log.i(TAG, "Broadcast receiver saving state (shutdown)");
|
||||
tunnelManager.saveState();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.widget.Toast;
|
||||
import com.wireguard.android.activity.MainActivity;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.android.model.TunnelManager;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -50,11 +49,6 @@ public class QuickTileService extends TileService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartListening() {
|
||||
Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback);
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.content.Intent;
|
||||
import android.databinding.CallbackRegistry;
|
||||
import android.databinding.CallbackRegistry.NotifierCallback;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.backend.GoBackend;
|
||||
@@ -54,11 +53,13 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
|
||||
// The selected tunnel must be set before the superclass method recreates fragments.
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (Application.getBackendType() == GoBackend.class) {
|
||||
final Intent intent = GoBackend.VpnService.prepare(this);
|
||||
if (intent != null)
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
Application.onHaveBackend(backend -> {
|
||||
if (backend.getClass() == GoBackend.class) {
|
||||
final Intent intent = GoBackend.VpnService.prepare(this);
|
||||
if (intent != null)
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,16 +6,14 @@
|
||||
|
||||
package com.wireguard.android.activity;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
@@ -95,12 +93,21 @@ public class SettingsActivity extends ThemeChangeAwareActivity {
|
||||
@Override
|
||||
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
if (Application.getBackendType() != WgQuickBackend.class) {
|
||||
Preference pref = getPreferenceManager().findPreference("tools_installer");
|
||||
getPreferenceScreen().removePreference(pref);
|
||||
pref = getPreferenceManager().findPreference("restore_on_boot");
|
||||
getPreferenceScreen().removePreference(pref);
|
||||
}
|
||||
final Preference wgQuickOnlyPrefs[] = {
|
||||
getPreferenceManager().findPreference("tools_installer"),
|
||||
getPreferenceManager().findPreference("restore_on_boot")
|
||||
};
|
||||
for (final Preference pref : wgQuickOnlyPrefs)
|
||||
pref.setVisible(false);
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
Application.onHaveBackend(backend -> {
|
||||
for (final Preference pref : wgQuickOnlyPrefs) {
|
||||
if (backend.getClass() == WgQuickBackend.class)
|
||||
pref.setVisible(true);
|
||||
else
|
||||
screen.removePreference(pref);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.wireguard.android.model.Tunnel.Statistics;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.SharedLibraryLoader;
|
||||
import com.wireguard.config.Config;
|
||||
import com.wireguard.config.IPCidr;
|
||||
import com.wireguard.config.InetNetwork;
|
||||
import com.wireguard.config.Interface;
|
||||
import com.wireguard.config.Peer;
|
||||
import com.wireguard.crypto.KeyEncoding;
|
||||
@@ -156,7 +156,7 @@ public final class GoBackend implements Backend {
|
||||
fmt.format("endpoint=%s\n", peer.getResolvedEndpointString());
|
||||
if (peer.getPersistentKeepalive() != 0)
|
||||
fmt.format("persistent_keepalive_interval=%d\n", peer.getPersistentKeepalive());
|
||||
for (final IPCidr addr : peer.getAllowedIPs()) {
|
||||
for (final InetNetwork addr : peer.getAllowedIPs()) {
|
||||
fmt.format("allowed_ip=%s\n", addr.toString());
|
||||
}
|
||||
}
|
||||
@@ -171,15 +171,15 @@ public final class GoBackend implements Backend {
|
||||
configureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
builder.setConfigureIntent(PendingIntent.getActivity(context, 0, configureIntent, 0));
|
||||
|
||||
for (final IPCidr addr : config.getInterface().getAddresses())
|
||||
builder.addAddress(addr.getAddress(), addr.getCidr());
|
||||
for (final InetNetwork addr : config.getInterface().getAddresses())
|
||||
builder.addAddress(addr.getAddress(), addr.getMask());
|
||||
|
||||
for (final InetAddress addr : config.getInterface().getDnses())
|
||||
builder.addDnsServer(addr.getHostAddress());
|
||||
|
||||
for (final Peer peer : config.getPeers()) {
|
||||
for (final IPCidr addr : peer.getAllowedIPs())
|
||||
builder.addRoute(addr.getAddress(), addr.getCidr());
|
||||
for (final InetNetwork addr : peer.getAllowedIPs())
|
||||
builder.addRoute(addr.getAddress(), addr.getMask());
|
||||
}
|
||||
|
||||
int mtu = config.getInterface().getMtu();
|
||||
|
||||
@@ -15,7 +15,7 @@ import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.util.Keyed;
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.android.util.ObservableKeyedList;
|
||||
import com.wireguard.android.widget.ToggleSwitch;
|
||||
import com.wireguard.android.widget.ToggleSwitch.OnBeforeCheckedChangeListener;
|
||||
|
||||
@@ -16,7 +16,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.android.util.Keyed;
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.android.util.ObservableKeyedList;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@@ -38,8 +38,6 @@ import com.wireguard.android.R;
|
||||
import com.wireguard.android.activity.TunnelCreatorActivity;
|
||||
import com.wireguard.android.databinding.TunnelListFragmentBinding;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.model.TunnelManager;
|
||||
import com.wireguard.android.util.AsyncWorker;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import android.support.annotation.Nullable;
|
||||
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.Keyed;
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -15,7 +15,6 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.BR;
|
||||
import com.wireguard.android.backend.Backend;
|
||||
import com.wireguard.android.configStore.ConfigStore;
|
||||
import com.wireguard.android.model.Tunnel.State;
|
||||
import com.wireguard.android.model.Tunnel.Statistics;
|
||||
@@ -47,7 +46,6 @@ public final class TunnelManager extends BaseObservable {
|
||||
private static final String KEY_RESTORE_ON_BOOT = "restore_on_boot";
|
||||
private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
|
||||
|
||||
private final Backend backend;
|
||||
private final ConfigStore configStore;
|
||||
private final ObservableSortedKeyedList<String, Tunnel> tunnels =
|
||||
new ObservableSortedKeyedArrayList<>(COMPARATOR);
|
||||
@@ -55,8 +53,7 @@ public final class TunnelManager extends BaseObservable {
|
||||
private boolean haveLoaded;
|
||||
private final ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>();
|
||||
|
||||
public TunnelManager(final Backend backend, final ConfigStore configStore) {
|
||||
this.backend = backend;
|
||||
public TunnelManager(final ConfigStore configStore) {
|
||||
this.configStore = configStore;
|
||||
}
|
||||
|
||||
@@ -86,12 +83,12 @@ public final class TunnelManager extends BaseObservable {
|
||||
tunnels.remove(tunnel);
|
||||
return Application.getAsyncWorker().runAsync(() -> {
|
||||
if (originalState == State.UP)
|
||||
backend.setState(tunnel, State.DOWN);
|
||||
Application.getBackend().setState(tunnel, State.DOWN);
|
||||
try {
|
||||
configStore.delete(tunnel.getName());
|
||||
} catch (final Exception e) {
|
||||
if (originalState == State.UP)
|
||||
backend.setState(tunnel, State.UP);
|
||||
Application.getBackend().setState(tunnel, State.UP);
|
||||
// Re-throw the exception to fail the completion.
|
||||
throw e;
|
||||
}
|
||||
@@ -116,12 +113,12 @@ public final class TunnelManager extends BaseObservable {
|
||||
}
|
||||
|
||||
CompletionStage<State> getTunnelState(final Tunnel tunnel) {
|
||||
return Application.getAsyncWorker().supplyAsync(() -> backend.getState(tunnel))
|
||||
return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getState(tunnel))
|
||||
.thenApply(tunnel::onStateChanged);
|
||||
}
|
||||
|
||||
CompletionStage<Statistics> getTunnelStatistics(final Tunnel tunnel) {
|
||||
return Application.getAsyncWorker().supplyAsync(() -> backend.getStatistics(tunnel))
|
||||
return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getStatistics(tunnel))
|
||||
.thenApply(tunnel::onStatisticsChanged);
|
||||
}
|
||||
|
||||
@@ -131,7 +128,7 @@ public final class TunnelManager extends BaseObservable {
|
||||
|
||||
public void onCreate() {
|
||||
Application.getAsyncWorker().supplyAsync(configStore::enumerate)
|
||||
.thenAcceptBoth(Application.getAsyncWorker().supplyAsync(backend::enumerate), this::onTunnelsLoaded)
|
||||
.thenAcceptBoth(Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().enumerate()), this::onTunnelsLoaded)
|
||||
.whenComplete(ExceptionLoggers.E);
|
||||
}
|
||||
|
||||
@@ -159,7 +156,7 @@ public final class TunnelManager extends BaseObservable {
|
||||
}
|
||||
|
||||
public void refreshTunnelStates() {
|
||||
Application.getAsyncWorker().supplyAsync(backend::enumerate)
|
||||
Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().enumerate())
|
||||
.thenAccept(running -> {
|
||||
for (final Tunnel tunnel : tunnels)
|
||||
tunnel.onStateChanged(running.contains(tunnel.getName()) ? State.UP : State.DOWN);
|
||||
@@ -207,7 +204,7 @@ public final class TunnelManager extends BaseObservable {
|
||||
|
||||
CompletionStage<Config> setTunnelConfig(final Tunnel tunnel, final Config config) {
|
||||
return Application.getAsyncWorker().supplyAsync(() -> {
|
||||
final Config appliedConfig = backend.applyConfig(tunnel, config);
|
||||
final Config appliedConfig = Application.getBackend().applyConfig(tunnel, config);
|
||||
return configStore.save(tunnel.getName(), appliedConfig);
|
||||
}).thenApply(tunnel::onConfigChanged);
|
||||
}
|
||||
@@ -227,11 +224,11 @@ public final class TunnelManager extends BaseObservable {
|
||||
tunnels.remove(tunnel);
|
||||
return Application.getAsyncWorker().supplyAsync(() -> {
|
||||
if (originalState == State.UP)
|
||||
backend.setState(tunnel, State.DOWN);
|
||||
Application.getBackend().setState(tunnel, State.DOWN);
|
||||
configStore.rename(tunnel.getName(), name);
|
||||
final String newName = tunnel.onNameChanged(name);
|
||||
if (originalState == State.UP)
|
||||
backend.setState(tunnel, State.UP);
|
||||
Application.getBackend().setState(tunnel, State.UP);
|
||||
return newName;
|
||||
}).whenComplete((newName, e) -> {
|
||||
// On failure, we don't know what state the tunnel might be in. Fix that.
|
||||
@@ -247,7 +244,7 @@ public final class TunnelManager extends BaseObservable {
|
||||
CompletionStage<State> setTunnelState(final Tunnel tunnel, final State state) {
|
||||
// Ensure the configuration is loaded before trying to use it.
|
||||
return tunnel.getConfigAsync().thenCompose(x ->
|
||||
Application.getAsyncWorker().supplyAsync(() -> backend.setState(tunnel, state))
|
||||
Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().setState(tunnel, state))
|
||||
).whenComplete((newState, e) -> {
|
||||
// Ensure onStateChanged is always called (failure or not), and with the correct state.
|
||||
tunnel.onStateChanged(e == null ? newState : tunnel.getState());
|
||||
|
||||
@@ -14,12 +14,11 @@ import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.activity.SettingsActivity;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.FragmentUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@@ -40,16 +39,6 @@ public class LogExporterPreference extends Preference {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
private static SettingsActivity getPrefActivity(final Preference preference) {
|
||||
final Context context = preference.getContext();
|
||||
if (context instanceof ContextThemeWrapper) {
|
||||
if (((ContextThemeWrapper) context).getBaseContext() instanceof SettingsActivity) {
|
||||
return ((SettingsActivity) ((ContextThemeWrapper) context).getBaseContext());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void exportLog() {
|
||||
Application.getAsyncWorker().supplyAsync(() -> {
|
||||
final File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
@@ -90,7 +79,7 @@ public class LogExporterPreference extends Preference {
|
||||
final String message = getContext().getString(R.string.log_export_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
Snackbar.make(
|
||||
getPrefActivity(this).findViewById(android.R.id.content),
|
||||
FragmentUtils.getPrefActivity(this).findViewById(android.R.id.content),
|
||||
message, Snackbar.LENGTH_LONG).show();
|
||||
setEnabled(true);
|
||||
} else {
|
||||
@@ -113,7 +102,7 @@ public class LogExporterPreference extends Preference {
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
getPrefActivity(this).ensurePermissions(
|
||||
FragmentUtils.getPrefActivity(this).ensurePermissions(
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
(permissions, granted) -> {
|
||||
if (granted.length > 0 && granted[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
@@ -9,11 +9,11 @@ package com.wireguard.android.preference;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.system.OsConstants;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.util.ToolsInstaller;
|
||||
|
||||
/**
|
||||
* Preference implementing a button that asynchronously runs {@code ToolsInstaller} and displays the
|
||||
@@ -43,28 +43,34 @@ public class ToolsInstallerPreference extends Preference {
|
||||
Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::areInstalled).whenComplete(this::onCheckResult);
|
||||
}
|
||||
|
||||
private void onCheckResult(final Integer result, final Throwable throwable) {
|
||||
setState(throwable == null && result == OsConstants.EALREADY ?
|
||||
State.ALREADY : initialState());
|
||||
private void onCheckResult(final int state, final Throwable throwable) {
|
||||
if (throwable != null || state == ToolsInstaller.ERROR)
|
||||
setState(State.INITIAL);
|
||||
else if ((state & ToolsInstaller.YES) == ToolsInstaller.YES)
|
||||
setState(State.ALREADY);
|
||||
else if ((state & (ToolsInstaller.MAGISK | ToolsInstaller.NO)) == (ToolsInstaller.MAGISK | ToolsInstaller.NO))
|
||||
setState(State.INITIAL_MAGISK);
|
||||
else if ((state & (ToolsInstaller.SYSTEM | ToolsInstaller.NO)) == (ToolsInstaller.SYSTEM | ToolsInstaller.NO))
|
||||
setState(State.INITIAL_SYSTEM);
|
||||
else
|
||||
setState(State.INITIAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
setState(workingState());
|
||||
setState(State.WORKING);
|
||||
Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::install).whenComplete(this::onInstallResult);
|
||||
}
|
||||
|
||||
private void onInstallResult(final Integer result, final Throwable throwable) {
|
||||
final State nextState;
|
||||
if (throwable != null)
|
||||
nextState = State.FAILURE;
|
||||
else if (result == OsConstants.EXIT_SUCCESS)
|
||||
nextState = successState();
|
||||
else if (result == OsConstants.EALREADY)
|
||||
nextState = State.ALREADY;
|
||||
setState(State.FAILURE);
|
||||
else if ((result & (ToolsInstaller.YES | ToolsInstaller.MAGISK)) == (ToolsInstaller.YES | ToolsInstaller.MAGISK))
|
||||
setState(State.SUCCESS_MAGISK);
|
||||
else if ((result & (ToolsInstaller.YES | ToolsInstaller.SYSTEM)) == (ToolsInstaller.YES | ToolsInstaller.SYSTEM))
|
||||
setState(State.SUCCESS_SYSTEM);
|
||||
else
|
||||
nextState = State.FAILURE;
|
||||
setState(nextState);
|
||||
setState(State.FAILURE);
|
||||
}
|
||||
|
||||
private void setState(@NonNull final State state) {
|
||||
@@ -76,26 +82,15 @@ public class ToolsInstallerPreference extends Preference {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
private State initialState() {
|
||||
return Application.getToolsInstaller().willInstallAsMagiskModule(false) ? State.INITIAL_MAGISK : State.INITIAL_SYSTEM;
|
||||
}
|
||||
private State workingState() {
|
||||
return Application.getToolsInstaller().willInstallAsMagiskModule(false) ? State.WORKING_MAGISK : State.WORKING_SYSTEM;
|
||||
}
|
||||
private State successState() {
|
||||
return Application.getToolsInstaller().willInstallAsMagiskModule(false) ? State.SUCCESS_MAGISK : State.SUCCESS_SYSTEM;
|
||||
}
|
||||
|
||||
private enum State {
|
||||
INITIAL(R.string.tools_installer_initial, true),
|
||||
ALREADY(R.string.tools_installer_already, false),
|
||||
FAILURE(R.string.tools_installer_failure, true),
|
||||
WORKING(R.string.tools_installer_working, false),
|
||||
INITIAL_SYSTEM(R.string.tools_installer_initial_system, true),
|
||||
SUCCESS_SYSTEM(R.string.tools_installer_success_system, false),
|
||||
WORKING_SYSTEM(R.string.tools_installer_working_system, false),
|
||||
INITIAL_MAGISK(R.string.tools_installer_initial_magisk, true),
|
||||
SUCCESS_MAGISK(R.string.tools_installer_success_magisk, false),
|
||||
WORKING_MAGISK(R.string.tools_installer_working_magisk, false);
|
||||
SUCCESS_MAGISK(R.string.tools_installer_success_magisk, false);
|
||||
|
||||
private final int messageResourceId;
|
||||
private final boolean shouldEnableView;
|
||||
|
||||
@@ -14,12 +14,6 @@ import android.util.AttributeSet;
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.BuildConfig;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.backend.Backend;
|
||||
import com.wireguard.android.backend.GoBackend;
|
||||
import com.wireguard.android.backend.WgQuickBackend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VersionPreference extends Preference {
|
||||
private String versionSummary;
|
||||
@@ -27,13 +21,14 @@ public class VersionPreference extends Preference {
|
||||
public VersionPreference(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
final Backend backend = Application.getBackend();
|
||||
versionSummary = getContext().getString(R.string.version_summary_checking, backend.getTypeName().toLowerCase());
|
||||
Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete((version, exception) -> {
|
||||
versionSummary = exception == null
|
||||
? getContext().getString(R.string.version_summary, backend.getTypeName(), version)
|
||||
: getContext().getString(R.string.version_summary_unknown, backend.getTypeName().toLowerCase());
|
||||
notifyChanged();
|
||||
Application.onHaveBackend(backend -> {
|
||||
versionSummary = getContext().getString(R.string.version_summary_checking, backend.getTypeName().toLowerCase());
|
||||
Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete((version, exception) -> {
|
||||
versionSummary = exception == null
|
||||
? getContext().getString(R.string.version_summary, backend.getTypeName(), version)
|
||||
: getContext().getString(R.string.version_summary_unknown, backend.getTypeName().toLowerCase());
|
||||
notifyChanged();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,12 @@ import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.wireguard.android.Application;
|
||||
import com.wireguard.android.R;
|
||||
import com.wireguard.android.activity.SettingsActivity;
|
||||
import com.wireguard.android.model.Tunnel;
|
||||
import com.wireguard.android.util.ExceptionLoggers;
|
||||
import com.wireguard.android.util.FragmentUtils;
|
||||
import com.wireguard.config.Config;
|
||||
|
||||
import java.io.File;
|
||||
@@ -47,16 +46,6 @@ public class ZipExporterPreference extends Preference {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
private static SettingsActivity getPrefActivity(final Preference preference) {
|
||||
final Context context = preference.getContext();
|
||||
if (context instanceof ContextThemeWrapper) {
|
||||
if (((ContextThemeWrapper) context).getBaseContext() instanceof SettingsActivity) {
|
||||
return ((SettingsActivity) ((ContextThemeWrapper) context).getBaseContext());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void exportZip() {
|
||||
final List<Tunnel> tunnels = new ArrayList<>(Application.getTunnelManager().getTunnels());
|
||||
final List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size());
|
||||
@@ -96,7 +85,7 @@ public class ZipExporterPreference extends Preference {
|
||||
final String message = getContext().getString(R.string.zip_export_error, error);
|
||||
Log.e(TAG, message, throwable);
|
||||
Snackbar.make(
|
||||
getPrefActivity(this).findViewById(android.R.id.content),
|
||||
FragmentUtils.getPrefActivity(this).findViewById(android.R.id.content),
|
||||
message, Snackbar.LENGTH_LONG).show();
|
||||
setEnabled(true);
|
||||
} else {
|
||||
@@ -119,7 +108,7 @@ public class ZipExporterPreference extends Preference {
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
getPrefActivity(this).ensurePermissions(
|
||||
FragmentUtils.getPrefActivity(this).ensurePermissions(
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
(permissions, granted) -> {
|
||||
if (granted.length > 0 && granted[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright © 2018 Harsh Shandilya <msfjarvis@gmail.com>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.wireguard.android.activity.SettingsActivity;
|
||||
|
||||
public class FragmentUtils {
|
||||
|
||||
public static SettingsActivity getPrefActivity(final Preference preference) {
|
||||
final Context context = preference.getContext();
|
||||
if (context instanceof ContextThemeWrapper) {
|
||||
if (((ContextThemeWrapper) context).getBaseContext() instanceof SettingsActivity) {
|
||||
return ((SettingsActivity) ((ContextThemeWrapper) context).getBaseContext());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ package com.wireguard.android.util;
|
||||
import android.databinding.ObservableArrayList;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -8,6 +8,9 @@ package com.wireguard.android.util;
|
||||
|
||||
import android.databinding.ObservableList;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.util.KeyedList;
|
||||
|
||||
/**
|
||||
* A list that is both keyed and observable.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,9 @@ package com.wireguard.android.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.util.SortedKeyedList;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
package com.wireguard.android.util;
|
||||
|
||||
import com.wireguard.util.Keyed;
|
||||
import com.wireguard.util.SortedKeyedList;
|
||||
|
||||
/**
|
||||
* A list that is both sorted/keyed and observable.
|
||||
*/
|
||||
|
||||
@@ -13,8 +13,8 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public final class SharedLibraryLoader {
|
||||
private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName();
|
||||
@@ -27,7 +27,7 @@ public final class SharedLibraryLoader {
|
||||
try {
|
||||
System.loadLibrary(libName);
|
||||
return;
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
} catch (final UnsatisfiedLinkError e) {
|
||||
Log.d(TAG, "Failed to load library normally, so attempting to extract from apk", e);
|
||||
noAbiException = e;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public final class SharedLibraryLoader {
|
||||
}
|
||||
System.load(f.getAbsolutePath());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
Log.d(TAG, "Failed to load library apk:/" + libZipPath, e);
|
||||
noAbiException = e;
|
||||
} finally {
|
||||
|
||||
@@ -25,6 +25,12 @@ import java.util.List;
|
||||
*/
|
||||
|
||||
public final class ToolsInstaller {
|
||||
public static final int ERROR = 0x0;
|
||||
public static final int YES = 0x1;
|
||||
public static final int NO = 0x2;
|
||||
public static final int MAGISK = 0x4;
|
||||
public static final int SYSTEM = 0x8;
|
||||
|
||||
private static final String[][] EXECUTABLES = {
|
||||
{"libwg.so", "wg"},
|
||||
{"libwg-quick.so", "wg-quick"},
|
||||
@@ -60,9 +66,8 @@ public final class ToolsInstaller {
|
||||
}
|
||||
|
||||
public int areInstalled() throws NoRootException {
|
||||
willInstallAsMagiskModule(true);
|
||||
if (INSTALL_DIR == null)
|
||||
return OsConstants.ENOENT;
|
||||
return ERROR;
|
||||
final StringBuilder script = new StringBuilder();
|
||||
for (final String[] names : EXECUTABLES) {
|
||||
script.append(String.format("cmp -s '%s' '%s' && ",
|
||||
@@ -71,9 +76,13 @@ public final class ToolsInstaller {
|
||||
}
|
||||
script.append("exit ").append(OsConstants.EALREADY).append(';');
|
||||
try {
|
||||
return Application.getRootShell().run(null, script.toString());
|
||||
final int ret = Application.getRootShell().run(null, script.toString());
|
||||
if (ret == OsConstants.EALREADY)
|
||||
return willInstallAsMagiskModule() ? YES | MAGISK : YES | SYSTEM;
|
||||
else
|
||||
return willInstallAsMagiskModule() ? NO | MAGISK : NO | SYSTEM;
|
||||
} catch (final IOException ignored) {
|
||||
return OsConstants.EXIT_FAILURE;
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +106,9 @@ public final class ToolsInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean willInstallAsMagiskModule(boolean checkForIt) {
|
||||
private boolean willInstallAsMagiskModule() {
|
||||
synchronized (lock) {
|
||||
if (installAsMagiskModule == null) {
|
||||
if (!checkForIt)
|
||||
throw new RuntimeException("Expected to already know whether this is a Magisk system");
|
||||
try {
|
||||
installAsMagiskModule = Application.getRootShell().run(null, "[ -d /sbin/.core/mirror -a -d /sbin/.core/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
|
||||
} catch (final Exception ignored) {
|
||||
@@ -123,9 +130,9 @@ public final class ToolsInstaller {
|
||||
new File(nativeLibraryDir, names[0]), destination, destination, destination));
|
||||
}
|
||||
try {
|
||||
return Application.getRootShell().run(null, script.toString());
|
||||
return Application.getRootShell().run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR;
|
||||
} catch (final IOException ignored) {
|
||||
return OsConstants.EXIT_FAILURE;
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,14 +151,14 @@ public final class ToolsInstaller {
|
||||
script.append("trap - INT TERM EXIT;");
|
||||
|
||||
try {
|
||||
return Application.getRootShell().run(null, script.toString());
|
||||
return Application.getRootShell().run(null, script.toString()) == 0 ? YES | MAGISK : ERROR;
|
||||
} catch (final IOException ignored) {
|
||||
return OsConstants.EXIT_FAILURE;
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
public int install() throws NoRootException {
|
||||
return willInstallAsMagiskModule(true) ? installMagisk() : installSystem();
|
||||
return willInstallAsMagiskModule() ? installMagisk() : installSystem();
|
||||
}
|
||||
|
||||
public int symlink() throws NoRootException {
|
||||
|
||||
@@ -8,13 +8,26 @@ package com.wireguard.android.widget.fab;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.*;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Shader.TileMode;
|
||||
import android.graphics.drawable.*;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable.ShaderFactory;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.graphics.drawable.shapes.OvalShape;
|
||||
import android.support.annotation.*;
|
||||
import android.support.annotation.ColorRes;
|
||||
import android.support.annotation.DimenRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.AppCompatImageButton;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
@@ -466,7 +466,7 @@ public class FloatingActionsMenu extends ViewGroup {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
public void setEnabled(final boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
|
||||
mAddButton.setEnabled(enabled);
|
||||
|
||||
@@ -13,11 +13,11 @@ import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public class TouchDelegateGroup extends TouchDelegate {
|
||||
private static final Rect USELESS_HACKY_RECT = new Rect();
|
||||
private final List<TouchDelegate> mTouchDelegates = new ArrayList<>();
|
||||
private final Collection<TouchDelegate> mTouchDelegates = new ArrayList<>();
|
||||
private TouchDelegate mCurrentTouchDelegate;
|
||||
private boolean mEnabled;
|
||||
|
||||
@@ -49,8 +49,7 @@ public class TouchDelegateGroup extends TouchDelegate {
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
for (int i = 0; i < mTouchDelegates.size(); i++) {
|
||||
final TouchDelegate touchDelegate = mTouchDelegates.get(i);
|
||||
for (final TouchDelegate touchDelegate : mTouchDelegates) {
|
||||
if (touchDelegate.onTouchEvent(event)) {
|
||||
mCurrentTouchDelegate = touchDelegate;
|
||||
return true;
|
||||
|
||||
@@ -8,9 +8,6 @@ package com.wireguard.config;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -36,7 +33,6 @@ enum Attribute {
|
||||
private static final String[] EMPTY_LIST = new String[0];
|
||||
private static final Map<String, Attribute> KEY_MAP;
|
||||
private static final Pattern LIST_SEPARATOR_PATTERN = Pattern.compile("\\s*,\\s*");
|
||||
private static final Method NUMERIC_ADDRESS_PARSER;
|
||||
private static final Pattern SEPARATOR_PATTERN = Pattern.compile("\\s|=");
|
||||
|
||||
static {
|
||||
@@ -46,14 +42,6 @@ enum Attribute {
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
NUMERIC_ADDRESS_PARSER = InetAddress.class.getMethod("parseNumericAddress", String.class);
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Pattern pattern;
|
||||
private final String token;
|
||||
|
||||
@@ -70,21 +58,6 @@ enum Attribute {
|
||||
return KEY_MAP.get(SEPARATOR_PATTERN.split(line)[0].toLowerCase());
|
||||
}
|
||||
|
||||
public static InetAddress parseIPString(final String address) {
|
||||
if (address == null || address.isEmpty())
|
||||
throw new IllegalArgumentException("Empty address");
|
||||
try {
|
||||
return (InetAddress) NUMERIC_ADDRESS_PARSER.invoke(null, address);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (final InvocationTargetException e) {
|
||||
if (e.getCause() instanceof IllegalArgumentException)
|
||||
throw (IllegalArgumentException) e.getCause();
|
||||
else
|
||||
throw new IllegalArgumentException(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] stringToList(final String string) {
|
||||
if (string == null)
|
||||
return EMPTY_LIST;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Locale;
|
||||
|
||||
public class IPCidr {
|
||||
private final InetAddress address;
|
||||
private int cidr;
|
||||
|
||||
@SuppressWarnings("MagicNumber")
|
||||
public IPCidr(String in) {
|
||||
cidr = -1;
|
||||
final int slash = in.lastIndexOf('/');
|
||||
if (slash != -1 && slash < in.length() - 1) {
|
||||
try {
|
||||
cidr = Integer.parseInt(in.substring(slash + 1), 10);
|
||||
in = in.substring(0, slash);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
address = Attribute.parseIPString(in);
|
||||
if ((address instanceof Inet6Address) && (cidr > 128 || cidr < 0))
|
||||
cidr = 128;
|
||||
else if ((address instanceof Inet4Address) && (cidr > 32 || cidr < 0))
|
||||
cidr = 32;
|
||||
}
|
||||
|
||||
public InetAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public int getCidr() {
|
||||
return cidr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.getDefault(), "%s/%d", address.getHostAddress(), cidr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
|
||||
public final class InetAddresses {
|
||||
private static final Method PARSER_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
// This method is only present on Android.
|
||||
PARSER_METHOD = InetAddress.class.getMethod("parseNumericAddress", String.class);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private InetAddresses() {
|
||||
// Prevent instantiation.
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static InetAddress parse(@Nullable final String address) {
|
||||
if (address == null || address.isEmpty())
|
||||
throw new IllegalArgumentException("Empty address");
|
||||
try {
|
||||
return (InetAddress) PARSER_METHOD.invoke(null, address);
|
||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Objects;
|
||||
|
||||
public class InetNetwork {
|
||||
private final InetAddress address;
|
||||
private final int mask;
|
||||
|
||||
public InetNetwork(@NonNull final String input) {
|
||||
final int slash = input.lastIndexOf('/');
|
||||
final int rawMask;
|
||||
final String rawAddress;
|
||||
if (slash >= 0) {
|
||||
rawMask = Integer.parseInt(input.substring(slash + 1), 10);
|
||||
rawAddress = input.substring(0, slash);
|
||||
} else {
|
||||
rawMask = -1;
|
||||
rawAddress = input;
|
||||
}
|
||||
address = InetAddresses.parse(rawAddress);
|
||||
final int maxMask = (address instanceof Inet4Address) ? 32 : 128;
|
||||
mask = rawMask >= 0 && rawMask <= maxMask ? rawMask : maxMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof InetNetwork))
|
||||
return false;
|
||||
final InetNetwork other = (InetNetwork) obj;
|
||||
return Objects.equals(address, other.address) && mask == other.mask;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public InetAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public int getMask() {
|
||||
return mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return address.hashCode() ^ mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return address.getHostAddress() + '/' + mask;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.util.List;
|
||||
*/
|
||||
|
||||
public class Interface {
|
||||
private final List<IPCidr> addressList;
|
||||
private final List<InetNetwork> addressList;
|
||||
private final List<InetAddress> dnsList;
|
||||
private Keypair keypair;
|
||||
private int listenPort;
|
||||
@@ -39,7 +39,7 @@ public class Interface {
|
||||
for (final String addr : addresses) {
|
||||
if (addr.isEmpty())
|
||||
throw new IllegalArgumentException("Address is empty");
|
||||
addressList.add(new IPCidr(addr));
|
||||
addressList.add(new InetNetwork(addr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class Interface {
|
||||
private void addDnses(final String[] dnses) {
|
||||
if (dnses != null && dnses.length > 0) {
|
||||
for (final String dns : dnses) {
|
||||
dnsList.add(Attribute.parseIPString(dns));
|
||||
dnsList.add(InetAddresses.parse(dns));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,8 +58,8 @@ public class Interface {
|
||||
return Attribute.iterableToString(addressList);
|
||||
}
|
||||
|
||||
public IPCidr[] getAddresses() {
|
||||
return addressList.toArray(new IPCidr[addressList.size()]);
|
||||
public InetNetwork[] getAddresses() {
|
||||
return addressList.toArray(new InetNetwork[addressList.size()]);
|
||||
}
|
||||
|
||||
private String getDnsString() {
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.Locale;
|
||||
*/
|
||||
|
||||
public class Peer {
|
||||
private final List<IPCidr> allowedIPsList;
|
||||
private final List<InetNetwork> allowedIPsList;
|
||||
private InetSocketAddress endpoint;
|
||||
private int persistentKeepalive;
|
||||
private String preSharedKey;
|
||||
@@ -41,13 +41,13 @@ public class Peer {
|
||||
private void addAllowedIPs(final String[] allowedIPs) {
|
||||
if (allowedIPs != null && allowedIPs.length > 0) {
|
||||
for (final String allowedIP : allowedIPs) {
|
||||
allowedIPsList.add(new IPCidr(allowedIP));
|
||||
allowedIPsList.add(new InetNetwork(allowedIP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IPCidr[] getAllowedIPs() {
|
||||
return allowedIPsList.toArray(new IPCidr[allowedIPsList.size()]);
|
||||
public InetNetwork[] getAllowedIPs() {
|
||||
return allowedIPsList.toArray(new InetNetwork[allowedIPsList.size()]);
|
||||
}
|
||||
|
||||
private String getAllowedIPsString() {
|
||||
|
||||
@@ -10,23 +10,23 @@ import java.util.Arrays;
|
||||
/**
|
||||
* Implementation of the Curve25519 elliptic curve algorithm.
|
||||
* <p>
|
||||
* This implementation was imported to WireGuard from noise-java:
|
||||
* https://github.com/rweather/noise-java
|
||||
* <p>
|
||||
* This implementation is based on that from arduinolibs:
|
||||
* https://github.com/rweather/arduinolibs
|
||||
* <p>
|
||||
* This implementation is copied verbatim from noise-java:
|
||||
* https://github.com/rweather/noise-java
|
||||
* <p>
|
||||
* Differences in this version are due to using 26-bit limbs for the
|
||||
* representation instead of the 8/16/32-bit limbs in the original.
|
||||
* <p>
|
||||
* References: http://cr.yp.to/ecdh.html, RFC 7748
|
||||
*/
|
||||
@SuppressWarnings("MagicNumber")
|
||||
@SuppressWarnings({"MagicNumber", "NonConstantFieldWithUpperCaseName", "SuspiciousNameCombination"})
|
||||
public final class Curve25519 {
|
||||
|
||||
// Numbers modulo 2^255 - 19 are broken up into ten 26-bit words.
|
||||
private static final int NUM_LIMBS_255BIT = 10;
|
||||
private static final int NUM_LIMBS_510BIT = 20;
|
||||
|
||||
private final int[] A;
|
||||
private final int[] AA;
|
||||
private final int[] B;
|
||||
@@ -152,6 +152,38 @@ public final class Curve25519 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts two numbers modulo 2^255 - 19.
|
||||
*
|
||||
* @param result The result.
|
||||
* @param x The first number to subtract.
|
||||
* @param y The second number to subtract.
|
||||
*/
|
||||
private static void sub(final int[] result, final int[] x, final int[] y) {
|
||||
int index;
|
||||
int borrow;
|
||||
|
||||
// Subtract y from x to generate the intermediate result.
|
||||
borrow = 0;
|
||||
for (index = 0; index < NUM_LIMBS_255BIT; ++index) {
|
||||
borrow = x[index] - y[index] - ((borrow >> 26) & 0x01);
|
||||
result[index] = borrow & 0x03FFFFFF;
|
||||
}
|
||||
|
||||
// If we had a borrow, then the result has gone negative and we
|
||||
// have to add 2^255 - 19 to the result to make it positive again.
|
||||
// The top bits of "borrow" will be all 1's if there is a borrow
|
||||
// or it will be all 0's if there was no borrow. Easiest is to
|
||||
// conditionally subtract 19 and then mask off the high bits.
|
||||
borrow = result[0] - ((-((borrow >> 26) & 0x01)) & 19);
|
||||
result[0] = borrow & 0x03FFFFFF;
|
||||
for (index = 1; index < NUM_LIMBS_255BIT; ++index) {
|
||||
borrow = result[index] - ((borrow >> 26) & 0x01);
|
||||
result[index] = borrow & 0x03FFFFFF;
|
||||
}
|
||||
result[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two numbers modulo 2^255 - 19.
|
||||
*
|
||||
@@ -160,8 +192,7 @@ public final class Curve25519 {
|
||||
* @param y The second number to add.
|
||||
*/
|
||||
private void add(final int[] result, final int[] x, final int[] y) {
|
||||
int carry;
|
||||
carry = x[0] + y[0];
|
||||
int carry = x[0] + y[0];
|
||||
result[0] = carry & 0x03FFFFFF;
|
||||
for (int index = 1; index < NUM_LIMBS_255BIT; ++index) {
|
||||
carry = (carry >> 26) + x[index] + y[index];
|
||||
@@ -200,12 +231,13 @@ public final class Curve25519 {
|
||||
*/
|
||||
private void evalCurve(final byte[] s) {
|
||||
int sposn = 31;
|
||||
int sbit = 6;
|
||||
int svalue = s[sposn] | 0x40;
|
||||
int swap = 0;
|
||||
|
||||
// Iterate over all 255 bits of "s" from the highest to the lowest.
|
||||
// We ignore the high bit of the 256-bit representation of "s".
|
||||
for (int sbit = 6; ; ) {
|
||||
while (true) {
|
||||
// Conditional swaps on entry to this bit but only if we
|
||||
// didn't swap on the previous bit.
|
||||
final int select = (svalue >> sbit) & 0x01;
|
||||
@@ -263,14 +295,12 @@ public final class Curve25519 {
|
||||
* @param y The second number to multiply.
|
||||
*/
|
||||
private void mul(final int[] result, final int[] x, final int[] y) {
|
||||
int i;
|
||||
|
||||
// Multiply the two numbers to create the intermediate result.
|
||||
long v = x[0];
|
||||
for (i = 0; i < NUM_LIMBS_255BIT; ++i) {
|
||||
for (int i = 0; i < NUM_LIMBS_255BIT; ++i) {
|
||||
t1[i] = v * y[i];
|
||||
}
|
||||
for (i = 1; i < NUM_LIMBS_255BIT; ++i) {
|
||||
for (int i = 1; i < NUM_LIMBS_255BIT; ++i) {
|
||||
v = x[i];
|
||||
for (int j = 0; j < (NUM_LIMBS_255BIT - 1); ++j) {
|
||||
t1[i + j] += v * y[j];
|
||||
@@ -281,7 +311,7 @@ public final class Curve25519 {
|
||||
// Propagate carries and convert back into 26-bit words.
|
||||
v = t1[0];
|
||||
t2[0] = ((int) v) & 0x03FFFFFF;
|
||||
for (i = 1; i < NUM_LIMBS_510BIT; ++i) {
|
||||
for (int i = 1; i < NUM_LIMBS_510BIT; ++i) {
|
||||
v = (v >> 26) + t1[i];
|
||||
t2[i] = ((int) v) & 0x03FFFFFF;
|
||||
}
|
||||
@@ -315,8 +345,6 @@ public final class Curve25519 {
|
||||
* @param x The argument.
|
||||
*/
|
||||
private void pow250(final int[] result, final int[] x) {
|
||||
int j;
|
||||
|
||||
// The big-endian hexadecimal expansion of (2^250 - 1) is:
|
||||
// 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
|
||||
//
|
||||
@@ -329,11 +357,11 @@ public final class Curve25519 {
|
||||
|
||||
// Build a pattern of 250 bits in length of repeated copies of 0000000001.
|
||||
square(A, x);
|
||||
for (j = 0; j < 9; ++j)
|
||||
for (int j = 0; j < 9; ++j)
|
||||
square(A, A);
|
||||
mul(result, A, x);
|
||||
for (int i = 0; i < 23; ++i) {
|
||||
for (j = 0; j < 10; ++j)
|
||||
for (int j = 0; j < 10; ++j)
|
||||
square(A, A);
|
||||
mul(result, result, A);
|
||||
}
|
||||
@@ -342,7 +370,7 @@ public final class Curve25519 {
|
||||
// the result to "fill in" the gaps in the pattern.
|
||||
square(A, result);
|
||||
mul(result, result, A);
|
||||
for (j = 0; j < 8; ++j) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
square(A, A);
|
||||
mul(result, result, A);
|
||||
}
|
||||
@@ -381,18 +409,14 @@ public final class Curve25519 {
|
||||
* @param size The number of limbs in the high order half of x.
|
||||
*/
|
||||
private void reduce(final int[] result, final int[] x, final int size) {
|
||||
int index;
|
||||
int limb;
|
||||
int carry;
|
||||
|
||||
// Calculate (x mod 2^255) + ((x / 2^255) * 19) which will
|
||||
// either produce the answer we want or it will produce a
|
||||
// value of the form "answer + j * (2^255 - 19)". There are
|
||||
// 5 left-over bits in the top-most limb of the bottom half.
|
||||
carry = 0;
|
||||
limb = x[NUM_LIMBS_255BIT - 1] >> 21;
|
||||
int carry = 0;
|
||||
int limb = x[NUM_LIMBS_255BIT - 1] >> 21;
|
||||
x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
|
||||
for (index = 0; index < size; ++index) {
|
||||
for (int index = 0; index < size; ++index) {
|
||||
limb += x[NUM_LIMBS_255BIT + index] << 5;
|
||||
carry += (limb & 0x03FFFFFF) * 19 + x[index];
|
||||
x[index] = carry & 0x03FFFFFF;
|
||||
@@ -402,7 +426,7 @@ public final class Curve25519 {
|
||||
if (size < NUM_LIMBS_255BIT) {
|
||||
// The high order half of the number is short; e.g. for mulA24().
|
||||
// Propagate the carry through the rest of the low order part.
|
||||
for (index = size; index < NUM_LIMBS_255BIT; ++index) {
|
||||
for (int index = size; index < NUM_LIMBS_255BIT; ++index) {
|
||||
carry += x[index];
|
||||
x[index] = carry & 0x03FFFFFF;
|
||||
carry >>= 26;
|
||||
@@ -417,7 +441,7 @@ public final class Curve25519 {
|
||||
// top 5 bits of the highest limb of the bottom half.
|
||||
carry = (x[NUM_LIMBS_255BIT - 1] >> 21) * 19;
|
||||
x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
|
||||
for (index = 0; index < NUM_LIMBS_255BIT; ++index) {
|
||||
for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
|
||||
carry += x[index];
|
||||
result[index] = carry & 0x03FFFFFF;
|
||||
carry >>= 26;
|
||||
@@ -436,14 +460,11 @@ public final class Curve25519 {
|
||||
* @param x The number to reduce, and the result.
|
||||
*/
|
||||
private void reduceQuick(final int[] x) {
|
||||
int index;
|
||||
int carry;
|
||||
|
||||
// Perform a trial subtraction of (2^255 - 19) from "x" which is
|
||||
// equivalent to adding 19 and subtracting 2^255. We add 19 here;
|
||||
// the subtraction of 2^255 occurs in the next step.
|
||||
carry = 19;
|
||||
for (index = 0; index < NUM_LIMBS_255BIT; ++index) {
|
||||
int carry = 19;
|
||||
for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
|
||||
carry += x[index];
|
||||
t2[index] = carry & 0x03FFFFFF;
|
||||
carry >>= 26;
|
||||
@@ -457,7 +478,7 @@ public final class Curve25519 {
|
||||
final int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01);
|
||||
final int nmask = ~mask;
|
||||
t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
|
||||
for (index = 0; index < NUM_LIMBS_255BIT; ++index)
|
||||
for (int index = 0; index < NUM_LIMBS_255BIT; ++index)
|
||||
x[index] = (x[index] & nmask) | (t2[index] & mask);
|
||||
}
|
||||
|
||||
@@ -470,36 +491,4 @@ public final class Curve25519 {
|
||||
private void square(final int[] result, final int[] x) {
|
||||
mul(result, x, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts two numbers modulo 2^255 - 19.
|
||||
*
|
||||
* @param result The result.
|
||||
* @param x The first number to subtract.
|
||||
* @param y The second number to subtract.
|
||||
*/
|
||||
private static void sub(final int[] result, final int[] x, final int[] y) {
|
||||
int index;
|
||||
int borrow;
|
||||
|
||||
// Subtract y from x to generate the intermediate result.
|
||||
borrow = 0;
|
||||
for (index = 0; index < NUM_LIMBS_255BIT; ++index) {
|
||||
borrow = x[index] - y[index] - ((borrow >> 26) & 0x01);
|
||||
result[index] = borrow & 0x03FFFFFF;
|
||||
}
|
||||
|
||||
// If we had a borrow, then the result has gone negative and we
|
||||
// have to add 2^255 - 19 to the result to make it positive again.
|
||||
// The top bits of "borrow" will be all 1's if there is a borrow
|
||||
// or it will be all 0's if there was no borrow. Easiest is to
|
||||
// conditionally subtract 19 and then mask off the high bits.
|
||||
borrow = result[0] - ((-((borrow >> 26) & 0x01)) & 19);
|
||||
result[0] = borrow & 0x03FFFFFF;
|
||||
for (index = 1; index < NUM_LIMBS_255BIT; ++index) {
|
||||
borrow = result[index] - ((borrow >> 26) & 0x01);
|
||||
result[index] = borrow & 0x03FFFFFF;
|
||||
}
|
||||
result[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
package com.wireguard.util;
|
||||
|
||||
/**
|
||||
* Interface for objects that have a identifying key of the given type.
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
package com.wireguard.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
+1
-2
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2018 Samuel Holland <samuel@sholland.org>
|
||||
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util;
|
||||
package com.wireguard.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -70,13 +70,12 @@
|
||||
<string name="tools_installer_already">wg and wg-quick are already installed</string>
|
||||
<string name="tools_installer_failure">Unable to install command-line tools (no root?)</string>
|
||||
<string name="tools_installer_initial">Install optional tools for scripting</string>
|
||||
<string name="tools_installer_working">Installing wg and wg-quick</string>
|
||||
<string name="tools_installer_initial_system">Install optional tools for scripting into the system partition</string>
|
||||
<string name="tools_installer_initial_magisk">Install optional tools for scripting as Magisk module</string>
|
||||
<string name="tools_installer_success_system">wg and wg-quick installed into the system partition</string>
|
||||
<string name="tools_installer_success_magisk">wg and wg-quick installed as a Magisk module (reboot required)</string>
|
||||
<string name="tools_installer_title">Install command line tools</string>
|
||||
<string name="tools_installer_working_system">Installing wg and wg-quick into the system partition</string>
|
||||
<string name="tools_installer_working_magisk">Installing wg and wg-quick as a Magisk module</string>
|
||||
<string name="tunnel_create_error">Unable to create tunnel: %s</string>
|
||||
<string name="tunnel_create_success">Successfully created tunnel “%s”</string>
|
||||
<string name="tunnel_rename_error">Unable to rename tunnel: %s</string>
|
||||
|
||||
+1
-1
Submodule app/tools/wireguard updated: a59b4d5808...e1167518ff
+1
-1
Submodule app/tools/wireguard-go updated: 0c976003c8...6b3b1c3b91
+1
-1
@@ -7,7 +7,7 @@ allprojects {
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip
|
||||
|
||||
Reference in New Issue
Block a user