diff --git a/app/src/main/java/com/wireguard/android/Application.java b/app/src/main/java/com/wireguard/android/Application.java index e8d0d7db..8a46a8f6 100644 --- a/app/src/main/java/com/wireguard/android/Application.java +++ b/app/src/main/java/com/wireguard/android/Application.java @@ -15,6 +15,7 @@ import com.wireguard.android.configStore.FileConfigStore; import com.wireguard.android.model.TunnelManager; import com.wireguard.android.util.AsyncWorker; import com.wireguard.android.util.RootShell; +import com.wireguard.android.util.ToolsInstaller; import java.util.concurrent.Executor; @@ -56,6 +57,8 @@ public class Application extends android.app.Application { SharedPreferences getPreferences(); + ToolsInstaller getToolsInstaller(); + TunnelManager getTunnelManager(); } 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 06d40ad5..3eb633ff 100644 --- a/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java +++ b/app/src/main/java/com/wireguard/android/activity/SettingsActivity.java @@ -1,14 +1,10 @@ package com.wireguard.android.activity; import android.app.Activity; -import android.content.Context; -import android.os.AsyncTask; import android.os.Bundle; -import android.preference.Preference; import android.preference.PreferenceFragment; import com.wireguard.android.R; -import com.wireguard.android.util.RootShell; /** * Interface for changing application-global persistent settings. @@ -30,74 +26,6 @@ public class SettingsActivity extends Activity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); - final Preference installTools = findPreference("install_cmd_line_tools"); - installTools.setOnPreferenceClickListener(preference -> { - new ToolsInstaller(preference).execute(); - return true; - }); - } - } - - private static final class ToolsInstaller extends AsyncTask { - private static final String[][] LIBRARY_NAMED_EXECUTABLES = { - {"libwg.so", "wg"}, - {"libwg-quick.so", "wg-quick"} - }; - - private final Context context; - private final Preference preference; - - private ToolsInstaller(final Preference preference) { - context = preference.getContext(); - this.preference = preference; - preference.setEnabled(false); - preference.setSummary(context.getString(R.string.install_cmd_line_tools_progress)); - } - - @Override - protected Integer doInBackground(final Void... voids) { - final String libDir = context.getApplicationInfo().nativeLibraryDir; - final StringBuilder cmd = new StringBuilder(); - - cmd.append("set -ex;"); - - for (final String[] libraryNamedExecutable : LIBRARY_NAMED_EXECUTABLES) { - final String arg1 = '\'' + libDir + '/' + libraryNamedExecutable[0] + '\''; - final String arg2 = "'/system/xbin/" + libraryNamedExecutable[1] + '\''; - - cmd.append(String.format("cmp -s %s %s && ", arg1, arg2)); - } - cmd.append("exit 114;"); - - cmd.append("trap 'mount -o ro,remount /system' EXIT;"); - cmd.append("mount -o rw,remount /system;"); - - for (final String[] libraryNamedExecutable : LIBRARY_NAMED_EXECUTABLES) { - final String arg1 = '\'' + libDir + '/' + libraryNamedExecutable[0] + '\''; - final String arg2 = "'/system/xbin/" + libraryNamedExecutable[1] + '\''; - cmd.append(String.format("cp %s %s; chmod 755 %s;", arg1, arg2, arg2)); - } - - return new RootShell(context).run(null, cmd.toString()); - } - - @Override - protected void onPostExecute(final Integer ret) { - final String status; - - switch (ret) { - case 0: - status = context.getString(R.string.install_cmd_line_tools_success); - break; - case 114 /* OsConstants.EALREADY */: - status = context.getString(R.string.install_cmd_line_tools_already); - break; - default: - status = context.getString(R.string.install_cmd_line_tools_failure); - break; - } - preference.setSummary(status); - preference.setEnabled(true); } } } diff --git a/app/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java b/app/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java new file mode 100644 index 00000000..c9dfa7b7 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java @@ -0,0 +1,91 @@ +package com.wireguard.android.preference; + +import android.content.Context; +import android.preference.Preference; +import android.system.OsConstants; +import android.util.AttributeSet; + +import com.wireguard.android.Application; +import com.wireguard.android.Application.ApplicationComponent; +import com.wireguard.android.R; +import com.wireguard.android.util.AsyncWorker; +import com.wireguard.android.util.ToolsInstaller; + +/** + * Preference implementing a button that asynchronously runs {@code ToolsInstaller} and displays the + * result as the preference summary. + */ + +public class ToolsInstallerPreference extends Preference { + private final AsyncWorker asyncWorker; + private final ToolsInstaller toolsInstaller; + private State state = State.INITIAL; + + public ToolsInstallerPreference(final Context context, final AttributeSet attrs) { + super(context, attrs); + final ApplicationComponent applicationComponent = Application.getComponent(); + asyncWorker = applicationComponent.getAsyncWorker(); + toolsInstaller = applicationComponent.getToolsInstaller(); + } + + public ToolsInstallerPreference(final Context context) { + this(context, null); + } + + private static State mapResultToState(final int scriptResult) { + if (scriptResult == OsConstants.EXIT_SUCCESS) + return State.SUCCESS; + else if (scriptResult == OsConstants.EALREADY) + return State.ALREADY; + else + return State.FAILURE; + } + + @Override + public CharSequence getSummary() { + return getContext().getString(state.messageResourceId); + } + + @Override + public CharSequence getTitle() { + return getContext().getString(getTitleRes()); + } + + @Override + public int getTitleRes() { + return R.string.tools_installer_title; + } + + @Override + protected void onClick() { + setState(State.WORKING); + asyncWorker.supplyAsync(toolsInstaller::install) + .thenApply(ToolsInstallerPreference::mapResultToState) + .thenAccept(this::setState); + } + + private void setState(final State state) { + if (this.state == state) + return; + this.state = state; + if (isEnabled() != state.shouldEnableView) + setEnabled(state.shouldEnableView); + notifyChanged(); + } + + private enum State { + ALREADY(R.string.tools_installer_already, false), + FAILURE(R.string.tools_installer_failure, true), + INITIAL(R.string.tools_installer_initial, true), + SUCCESS(R.string.tools_installer_success, false), + WORKING(R.string.tools_installer_working, false); + + private final int messageResourceId; + private final boolean shouldEnableView; + + State(final int messageResourceId, final boolean shouldEnableView) { + this.messageResourceId = messageResourceId; + this.shouldEnableView = shouldEnableView; + } + } +} diff --git a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java new file mode 100644 index 00000000..8496a310 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java @@ -0,0 +1,70 @@ +package com.wireguard.android.util; + +import android.content.Context; +import android.system.OsConstants; + +import com.wireguard.android.Application.ApplicationContext; +import com.wireguard.android.Application.ApplicationScope; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import javax.inject.Inject; + +/** + * Helper to install WireGuard tools to the system partition. + */ + +@ApplicationScope +public final class ToolsInstaller { + private static final String[][] EXECUTABLES = { + {"libwg.so", "wg"}, + {"libwg-quick.so", "wg-quick"}, + }; + private static final File[] INSTALL_DIRS = { + new File("/system/xbin"), + new File("/system/bin"), + }; + + private final String nativeLibraryDir; + private final RootShell rootShell; + + @Inject + public ToolsInstaller(@ApplicationContext final Context context, final RootShell rootShell) { + nativeLibraryDir = context.getApplicationInfo().nativeLibraryDir; + this.rootShell = rootShell; + } + + private static File getInstallDir() { + final String path = System.getenv("PATH"); + if (path == null) + return INSTALL_DIRS[0]; + final List paths = Arrays.asList(path.split(":")); + for (final File dir : INSTALL_DIRS) + if (paths.contains(dir.getPath()) && dir.isDirectory()) + return dir; + return null; + } + + public int install() { + final File installDir = getInstallDir(); + if (installDir == null) + return OsConstants.ENOENT; + final StringBuilder script = new StringBuilder("set -ex; "); + for (final String[] names : EXECUTABLES) { + script.append(String.format("cmp -s '%s' '%s' && ", + new File(nativeLibraryDir, names[0]), + new File(installDir, names[1]))); + } + script.append("exit ").append(OsConstants.EALREADY).append("; "); + script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; "); + for (final String[] names : EXECUTABLES) { + script.append(String.format("cp %s %s; chmod 755 %s; ", + new File(nativeLibraryDir, names[0]), + new File(installDir, names[1]), + new File(installDir, names[1]))); + } + return rootShell.run(null, script.toString()); + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c897e345..48030788 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,16 +58,16 @@ Public key Restore on boot Restore previously enabled configurations on boot - Install command line tools - Install optional tools for scripting into /system/xbin - wg and wg-quick installed into /system/xbin - Installing wg and wg-quick into /system/xbin - wg and wg-quick are already installed - Command line tools could not be installed Save Settings Status Toggle + wg and wg-quick are already installed + Command line tools could not be installed (no root?) + Install optional tools for scripting into the system partition + wg and wg-quick installed into the system partition + Install command line tools + Installing wg and wg-quick into the system partition Last change never diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index e02572dd..290f7086 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -5,8 +5,5 @@ android:key="restore_on_boot" android:summary="@string/restore_on_boot_summary" android:title="@string/restore_on_boot" /> - +