Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>master
@@ -2,9 +2,14 @@ package com.wireguard.android; | |||
import android.app.Activity; | |||
import android.app.FragmentTransaction; | |||
import android.content.Context; | |||
import android.os.AsyncTask; | |||
import android.os.Bundle; | |||
import android.preference.Preference; | |||
import android.preference.PreferenceFragment; | |||
import com.wireguard.android.backends.RootShell; | |||
public class SettingsActivity extends Activity { | |||
@Override | |||
protected void onCreate(final Bundle savedInstanceState) { | |||
@@ -22,6 +27,77 @@ public class SettingsActivity extends Activity { | |||
addPreferencesFromResource(R.xml.preferences); | |||
if (getArguments() != null && getArguments().getBoolean("showQuickTile")) | |||
((ConfigListPreference) findPreference("primary_config")).show(); | |||
final Preference installTools = findPreference("install_cmd_line_tools"); | |||
installTools.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { | |||
public boolean onPreferenceClick(Preference preference) { | |||
new ToolsInstaller(installTools).execute(); | |||
return true; | |||
} | |||
}); | |||
} | |||
} | |||
private static class ToolsInstaller extends AsyncTask<Void, Void, Integer> { | |||
Preference installTools; | |||
public ToolsInstaller(Preference installTools) { | |||
this.installTools = installTools; | |||
installTools.setEnabled(false); | |||
installTools.setSummary(installTools.getContext().getString(R.string.install_cmd_line_tools_progress)); | |||
} | |||
private static final String[][] libraryNamedExecutables = { | |||
{ "libwg.so", "wg" }, | |||
{ "libwg-quick.so", "wg-quick" } | |||
}; | |||
@Override | |||
protected Integer doInBackground(final Void... voids) { | |||
final Context context = installTools.getContext(); | |||
final String libDir = context.getApplicationInfo().nativeLibraryDir; | |||
final StringBuilder cmd = new StringBuilder(); | |||
cmd.append("set -ex;"); | |||
for (final String[] libraryNamedExecutable : libraryNamedExecutables) { | |||
final String arg1 = "'" + libDir + "/" + libraryNamedExecutable[0] + "'"; | |||
final String arg2 = "'/system/xbin/" + libraryNamedExecutable[1] + "'"; | |||
cmd.append(String.format("diff %s %s && ", arg1, arg2)); | |||
} | |||
cmd.append("exit 114;"); | |||
cmd.append("trap 'mount -o remount,ro /system' EXIT;"); | |||
cmd.append("mount -o remount,rw /system;"); | |||
for (final String[] libraryNamedExecutable : libraryNamedExecutables) { | |||
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 Context context = installTools.getContext(); | |||
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; | |||
} | |||
installTools.setSummary(status); | |||
installTools.setEnabled(true); | |||
} | |||
} | |||
} |
@@ -18,7 +18,7 @@ import java.util.regex.Matcher; | |||
* Helper class for running commands as root. | |||
*/ | |||
class RootShell { | |||
public class RootShell { | |||
/** | |||
* Setup commands that are run at the beginning of each root shell. The trap command ensures | |||
* access to the return value of the last command, since su itself always exits with 0. | |||
@@ -33,23 +33,23 @@ class RootShell { | |||
private final String preamble; | |||
RootShell(final Context context) { | |||
public RootShell(final Context context) { | |||
final String binDir = context.getCacheDir().getPath() + "/bin"; | |||
final String tmpDir = context.getCacheDir().getPath() + "/tmp"; | |||
final String libDir = context.getApplicationInfo().nativeLibraryDir; | |||
new File(binDir).mkdirs(); | |||
new File(tmpDir).mkdirs(); | |||
preamble = String.format("export PATH=\"%s:$PATH\" TMPDIR=\"%s\";", binDir, tmpDir); | |||
final String libDir = context.getApplicationInfo().nativeLibraryDir; | |||
String symlinkCommand = "set -ex;"; | |||
StringBuilder builder = new StringBuilder(); | |||
for (final String[] libraryNamedExecutable : libraryNamedExecutables) { | |||
final String args = "'" + libDir + "/" + libraryNamedExecutable[0] + "' '" + binDir + "/" + libraryNamedExecutable[1] + "'"; | |||
symlinkCommand += "ln -f " + args + " || ln -sf " + args + ";"; | |||
final String arg1 = "'" + libDir + "/" + libraryNamedExecutable[0] + "'"; | |||
final String arg2 = "'" + binDir + "/" + libraryNamedExecutable[1] + "'"; | |||
builder.append(String.format("[ %s -ef %s ] || ln -sf %s %s || exit 31;", arg1, arg2, arg1, arg2)); | |||
} | |||
if (run(null, symlinkCommand) != 0) | |||
Log.e(TAG, "Unable to establish symlinks for important executables."); | |||
builder.append(String.format("export PATH=\"%s:$PATH\" TMPDIR=\"%s\";", binDir, tmpDir)); | |||
preamble = builder.toString(); | |||
} | |||
/** | |||
@@ -60,7 +60,7 @@ class RootShell { | |||
* @param command Command to run as root. | |||
* @return The exit value of the last command run, or -1 if there was an internal error. | |||
*/ | |||
int run(final List<String> output, final String command) { | |||
public int run(final List<String> output, final String command) { | |||
int exitValue = -1; | |||
try { | |||
final ProcessBuilder builder = new ProcessBuilder(); | |||
@@ -14,7 +14,6 @@ import android.os.IBinder; | |||
import android.preference.PreferenceManager; | |||
import android.provider.OpenableColumns; | |||
import android.service.quicksettings.TileService; | |||
import android.system.ErrnoException; | |||
import android.system.OsConstants; | |||
import android.util.Log; | |||
import android.widget.Toast; | |||
@@ -60,6 +60,12 @@ | |||
<string name="public_key_description">WireGuard 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> | |||
@@ -9,4 +9,8 @@ | |||
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" /> | |||
</PreferenceScreen> |