ToolsInstaller: Extract to its own classes
Signed-off-by: Samuel Holland <samuel@sholland.org>
This commit is contained in:
parent
1f30e133d6
commit
08cca56388
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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<Void, Void, Integer> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<String> 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());
|
||||
}
|
||||
}
|
@ -58,16 +58,16 @@
|
||||
<string name="public_key_description">Public key</string>
|
||||
<string name="restore_on_boot">Restore on boot</string>
|
||||
<string name="restore_on_boot_summary">Restore previously enabled configurations on boot</string>
|
||||
<string name="install_cmd_line_tools">Install command line tools</string>
|
||||
<string name="install_cmd_line_tools_summary">Install optional tools for scripting into /system/xbin</string>
|
||||
<string name="install_cmd_line_tools_success">wg and wg-quick installed into /system/xbin</string>
|
||||
<string name="install_cmd_line_tools_progress">Installing wg and wg-quick into /system/xbin</string>
|
||||
<string name="install_cmd_line_tools_already">wg and wg-quick are already installed</string>
|
||||
<string name="install_cmd_line_tools_failure">Command line tools could not be installed</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="status">Status</string>
|
||||
<string name="toggle">Toggle</string>
|
||||
<string name="tools_installer_already">wg and wg-quick are already installed</string>
|
||||
<string name="tools_installer_failure">Command line tools could not be installed (no root?)</string>
|
||||
<string name="tools_installer_initial">Install optional tools for scripting into the system partition</string>
|
||||
<string name="tools_installer_success">wg and wg-quick installed into the system partition</string>
|
||||
<string name="tools_installer_title">Install command line tools</string>
|
||||
<string name="tools_installer_working">Installing wg and wg-quick into the system partition</string>
|
||||
<string name="last_change">Last change</string>
|
||||
<string name="never">never</string>
|
||||
</resources>
|
||||
|
@ -5,8 +5,5 @@
|
||||
android:key="restore_on_boot"
|
||||
android:summary="@string/restore_on_boot_summary"
|
||||
android:title="@string/restore_on_boot" />
|
||||
<Preference
|
||||
android:key="install_cmd_line_tools"
|
||||
android:summary="@string/install_cmd_line_tools_summary"
|
||||
android:title="@string/install_cmd_line_tools" />
|
||||
<com.wireguard.android.preference.ToolsInstallerPreference />
|
||||
</PreferenceScreen>
|
||||
|
Loading…
Reference in New Issue
Block a user