From e8891d775b613781529e2f86cb063ee2f16a229f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 14 Jun 2018 04:59:24 +0200 Subject: 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 --- .../java/com/wireguard/android/Application.java | 55 ++++++++++++++++------ .../wireguard/android/BootShutdownReceiver.java | 28 ++++++----- .../wireguard/android/activity/BaseActivity.java | 12 +++-- .../android/activity/SettingsActivity.java | 22 ++++++--- .../com/wireguard/android/model/TunnelManager.java | 25 +++++----- .../android/preference/VersionPreference.java | 15 +++--- 6 files changed, 98 insertions(+), 59 deletions(-) (limited to 'app/src') 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 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 tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR); @@ -55,8 +53,7 @@ public final class TunnelManager extends BaseObservable { private boolean haveLoaded; private final ArrayList> 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 getTunnelState(final Tunnel tunnel) { - return Application.getAsyncWorker().supplyAsync(() -> backend.getState(tunnel)) + return Application.getAsyncWorker().supplyAsync(() -> Application.getBackend().getState(tunnel)) .thenApply(tunnel::onStateChanged); } CompletionStage 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 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 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(); + }); }); } -- cgit v1.2.3-59-g8ed1b