summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2018-06-14 04:59:24 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2018-06-14 05:06:18 +0200
commite8891d775b613781529e2f86cb063ee2f16a229f (patch)
tree069ddd86ba338411653a5c26e6b3570b6f507fbf
parent0f128f99a1175b229fb82e2a802b1ea3fe4aedf5 (diff)
global: supply backend asynchronously
We can't block for IO, so move everything to async workers or to callbacks. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--app/src/main/java/com/wireguard/android/Application.java55
-rw-r--r--app/src/main/java/com/wireguard/android/BootShutdownReceiver.java28
-rw-r--r--app/src/main/java/com/wireguard/android/activity/BaseActivity.java12
-rw-r--r--app/src/main/java/com/wireguard/android/activity/SettingsActivity.java22
-rw-r--r--app/src/main/java/com/wireguard/android/model/TunnelManager.java25
-rw-r--r--app/src/main/java/com/wireguard/android/preference/VersionPreference.java15
6 files changed, 98 insertions, 59 deletions
diff --git a/app/src/main/java/com/wireguard/android/Application.java b/app/src/main/java/com/wireguard/android/Application.java
index f79e4f32..d9e4bfa9 100644
--- a/app/src/main/java/com/wireguard/android/Application.java
+++ b/app/src/main/java/com/wireguard/android/Application.java
@@ -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 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,16 +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()) {
- try {
- rootShell.start();
- backend = new WgQuickBackend(getApplicationContext());
- } catch (final Exception ignored) { }
- }
- if (backend == null)
- backend = new GoBackend(getApplicationContext());
-
- tunnelManager = new TunnelManager(backend, configStore);
+ tunnelManager = new TunnelManager(configStore);
+ asyncWorker.runAsync(Application::getBackend);
tunnelManager.onCreate();
}
}
diff --git a/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java b/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java
index f3ed3f08..fd7aa9f9 100644
--- a/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java
+++ b/app/src/main/java/com/wireguard/android/BootShutdownReceiver.java
@@ -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();
+ }
+ });
}
}
diff --git a/app/src/main/java/com/wireguard/android/activity/BaseActivity.java b/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
index 7d01ad1f..554eb915 100644
--- a/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/BaseActivity.java
@@ -54,11 +54,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
diff --git a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
index 03ae11a0..d91123b4 100644
--- a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java
@@ -16,6 +16,7 @@ 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 +96,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);
+ }
+ });
}
}
}
diff --git a/app/src/main/java/com/wireguard/android/model/TunnelManager.java b/app/src/main/java/com/wireguard/android/model/TunnelManager.java
index 6747d449..d0b30415 100644
--- a/app/src/main/java/com/wireguard/android/model/TunnelManager.java
+++ b/app/src/main/java/com/wireguard/android/model/TunnelManager.java
@@ -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());
diff --git a/app/src/main/java/com/wireguard/android/preference/VersionPreference.java b/app/src/main/java/com/wireguard/android/preference/VersionPreference.java
index ab51644f..6b21977a 100644
--- a/app/src/main/java/com/wireguard/android/preference/VersionPreference.java
+++ b/app/src/main/java/com/wireguard/android/preference/VersionPreference.java
@@ -27,13 +27,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();
+ });
});
}