diff --git a/app/src/main/java/com/wireguard/android/QuickTileService.java b/app/src/main/java/com/wireguard/android/QuickTileService.java
index 0468f83f..7cbcf559 100644
--- a/app/src/main/java/com/wireguard/android/QuickTileService.java
+++ b/app/src/main/java/com/wireguard/android/QuickTileService.java
@@ -16,7 +16,6 @@ import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.ExceptionLoggers;
-import com.wireguard.android.util.RootShell;
import java.util.Objects;
@@ -63,16 +62,13 @@ public class QuickTileService extends TileService {
tunnelManager.removeOnPropertyChangedCallback(onTunnelChangedCallback);
}
- @SuppressWarnings("unused")
- private Void onToggleFinished(final State state, final Throwable throwable) {
+ private void onToggleFinished(final State state, final Throwable throwable) {
if (throwable == null)
- return null;
- Log.e(TAG, "Cannot toggle tunnel", throwable);
- final String message = throwable instanceof RootShell.NoRootException ?
- getApplicationContext().getString(R.string.error_rootshell) :
- getApplicationContext().getString(R.string.error_toggle) + ": " + throwable.getMessage();
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
- return null;
+ return;
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ final String message = getString(R.string.toggle_error, error);
+ Log.e(TAG, message, throwable);
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
private void updateTile() {
diff --git a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
index 5ac27af4..cb5eae5d 100644
--- a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
+++ b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
@@ -55,7 +55,7 @@ public final class WgQuickBackend implements Backend {
try {
if (rootShell.run(output, "wg show interfaces") != 0 || output.isEmpty())
return Collections.emptySet();
- } catch (Exception e) {
+ } catch (final Exception ignored) {
return Collections.emptySet();
}
// wg puts all interface names on the same line. Split them into separate elements.
diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelController.java b/app/src/main/java/com/wireguard/android/fragment/TunnelController.java
index 1a9fed6b..fd265d20 100644
--- a/app/src/main/java/com/wireguard/android/fragment/TunnelController.java
+++ b/app/src/main/java/com/wireguard/android/fragment/TunnelController.java
@@ -19,7 +19,6 @@ import com.wireguard.android.databinding.TunnelListItemBinding;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.util.ExceptionLoggers;
-import com.wireguard.android.util.RootShell;
/**
* Helper method shared by TunnelListFragment and TunnelDetailFragment.
@@ -48,7 +47,6 @@ public final class TunnelController {
tunnel.setState(State.of(checked)).whenComplete((state, throwable) -> {
if (throwable == null)
return;
- Log.e(TAG, "Cannot set state of tunnel " + tunnel.getName(), throwable);
final Context context = view.getContext();
if (throwable instanceof ErrnoException
&& ((ErrnoException) throwable).errno == OsConstants.ENODEV) {
@@ -61,13 +59,13 @@ public final class TunnelController {
// Make links work.
((TextView) dialog.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
- } else if (throwable instanceof RootShell.NoRootException) {
- Snackbar.make(view, R.string.error_rootshell, Snackbar.LENGTH_LONG).show();
+ Log.e(TAG, "WireGuard not supported");
} else {
- final String message =
- context.getString(checked ? R.string.error_up : R.string.error_down) + ": "
- + ExceptionLoggers.unwrap(throwable).getMessage();
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ final int messageResId = checked ? R.string.error_up : R.string.error_down;
+ final String message = context.getString(messageResId, error);
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
+ Log.e(TAG, message, throwable);
}
});
}
diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java b/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java
index f1cd7263..f72a6aea 100644
--- a/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java
+++ b/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java
@@ -60,17 +60,19 @@ public class TunnelEditorFragment extends BaseFragment {
}
private void onConfigSaved(final Config config, final Throwable throwable) {
- if (throwable != null) {
- Log.e(TAG, "Cannot save configuration", throwable);
- final String message = "Cannot save configuration: "
- + ExceptionLoggers.unwrap(throwable).getMessage();
+ final String message;
+ if (throwable == null) {
+ message = getString(R.string.config_save_success, localTunnel.getName());
+ Log.d(TAG, message);
+ onFinished();
+ } else {
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ message = getString(R.string.config_save_error, localTunnel.getName(), error);
+ Log.e(TAG, message, throwable);
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
Snackbar.make(container, message, Snackbar.LENGTH_LONG).show();
}
- } else {
- Log.d(TAG, "Successfully saved configuration for " + localTunnel.getName());
- onFinished();
}
}
@@ -191,38 +193,40 @@ public class TunnelEditorFragment extends BaseFragment {
}
private void onTunnelCreated(final Tunnel tunnel, final Throwable throwable) {
- if (throwable != null) {
- Log.e(TAG, "Cannot create tunnel", throwable);
- final String message = "Cannot create tunnel: "
- + ExceptionLoggers.unwrap(throwable).getMessage();
+ final String message;
+ if (throwable == null) {
+ message = getString(R.string.tunnel_create_success, tunnel.getName());
+ Log.d(TAG, message);
+ onFinished();
+ } else {
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ message = getString(R.string.tunnel_create_error, error);
+ Log.e(TAG, message, throwable);
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
Snackbar.make(container, message, Snackbar.LENGTH_LONG).show();
}
- } else {
- Log.d(TAG, "Successfully created tunnel " + tunnel.getName());
- localTunnel = tunnel;
- onFinished();
}
}
-
private void onTunnelRenamed(final Tunnel tunnel, final Throwable throwable) {
- if (throwable != null) {
- Log.e(TAG, "Cannot rename tunnel", throwable);
- final String message = "Cannot rename tunnel: "
- + ExceptionLoggers.unwrap(throwable).getMessage();
+ final String message;
+ if (throwable == null) {
+ message = getString(R.string.tunnel_rename_success, localTunnel.getName(),
+ tunnel.getName());
+ Log.d(TAG, message);
+ localTunnel = tunnel;
+ // Now save the rest of configuration changes.
+ Log.d(TAG, "Attempting to save config of renamed tunnel " + tunnel.getName());
+ tunnel.setConfig(localConfig).whenComplete(this::onConfigSaved);
+ } else {
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ message = getString(R.string.tunnel_rename_error, error);
+ Log.e(TAG, message, throwable);
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
Snackbar.make(container, message, Snackbar.LENGTH_LONG).show();
}
- } else {
- Log.d(TAG, "Successfully renamed tunnel to " + tunnel.getName());
- localTunnel = tunnel;
- // Now save the rest of configuration changes.
- Log.d(TAG, "Attempting to save config of " + tunnel.getName());
- tunnel.setConfig(localConfig)
- .whenComplete(this::onConfigSaved);
}
}
diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java b/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
index 4634889c..79bf5d02 100644
--- a/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
+++ b/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
@@ -148,13 +148,12 @@ public class TunnelListFragment extends BaseFragment {
private void onTunnelDeletionFinished(final Integer count, final Throwable throwable) {
final String message;
- final String plural = count == 1 ? "" : "s";
if (throwable == null) {
- message = "Successfully deleted " + count + " tunnel" + plural;
+ message = getResources().getQuantityString(R.plurals.delete_success, count, count);
} else {
- message = "Unable to delete tunnel" + plural + ": "
- + ExceptionLoggers.unwrap(throwable).getMessage();
- Log.e(TAG, "Cannot delete tunnel" + plural, throwable);
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ message = getResources().getQuantityString(R.plurals.delete_error, count, count, error);
+ Log.e(TAG, message, throwable);
}
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
@@ -165,11 +164,11 @@ public class TunnelListFragment extends BaseFragment {
private void onTunnelImportFinished(final Tunnel tunnel, final Throwable throwable) {
final String message;
if (throwable == null) {
- message = "Successfully imported tunnel '" + tunnel.getName() + '\'';
+ message = getString(R.string.import_success, tunnel.getName());
} else {
- message = "Cannot import tunnel: "
- + ExceptionLoggers.unwrap(throwable).getMessage();
- Log.e(TAG, "Cannot import tunnel", throwable);
+ final String error = ExceptionLoggers.unwrap(throwable).getMessage();
+ message = getString(R.string.import_error, error);
+ Log.e(TAG, message, throwable);
}
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
@@ -245,7 +244,7 @@ public class TunnelListFragment extends BaseFragment {
private void updateTitle(final ActionMode mode) {
final int count = (int) getCheckedPositions().count();
- mode.setTitle(resources.getQuantityString(R.plurals.list_delete_title, count, count));
+ mode.setTitle(resources.getQuantityString(R.plurals.delete_title, count, count));
}
}
diff --git a/app/src/main/java/com/wireguard/android/util/RootShell.java b/app/src/main/java/com/wireguard/android/util/RootShell.java
index 116fbd2c..2c498131 100644
--- a/app/src/main/java/com/wireguard/android/util/RootShell.java
+++ b/app/src/main/java/com/wireguard/android/util/RootShell.java
@@ -7,12 +7,14 @@ import android.util.Log;
import com.wireguard.android.Application.ApplicationContext;
import com.wireguard.android.Application.ApplicationScope;
+import com.wireguard.android.R;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.OutputStreamWriter;
import java.io.InputStreamReader;
import java.io.File;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
@@ -34,6 +36,7 @@ public class RootShell {
{"libwg-quick.so", "wg-quick"}
};
+ private final String exceptionMessage;
private final String preamble;
private BufferedWriter stdin;
@@ -59,6 +62,7 @@ public class RootShell {
builder.append(String.format("export PATH=\"%s:$PATH\" TMPDIR=\"%s\";", binDir, tmpDir));
builder.append("id;\n");
+ exceptionMessage = context.getString(R.string.error_root);
preamble = builder.toString();
}
@@ -72,7 +76,7 @@ public class RootShell {
return false;
}
- private void ensureRoot() throws Exception {
+ private void ensureRoot() throws ErrnoException, IOException, NoRootException {
try {
if (process != null) {
process.exitValue();
@@ -83,7 +87,7 @@ public class RootShell {
}
if (!isExecutable("su"))
- throw new NoRootException();
+ throw new NoRootException(exceptionMessage);
try {
final ProcessBuilder builder = new ProcessBuilder();
@@ -103,8 +107,8 @@ public class RootShell {
int errno = process.exitValue();
String line;
while ((line = stderr.readLine()) != null) {
- if (line.contains("Permission denied"))
- throw new NoRootException();
+ if (line.contains("Permission denied"))
+ throw new NoRootException(exceptionMessage);
}
throw new ErrnoException("Unknown error when obtaining root access", errno);
} catch (IllegalThreadStateException e) {
@@ -112,7 +116,7 @@ public class RootShell {
}
if (id == null || !id.contains("uid=0"))
- throw new NoRootException();
+ throw new NoRootException(exceptionMessage);
} catch (Exception e) {
Log.w(TAG, "Session failed with exception", e);
process.destroy();
@@ -121,7 +125,7 @@ public class RootShell {
if (match.find()) {
final int errno = Integer.valueOf(match.group(1));
if (errno == OsConstants.EACCES)
- throw new NoRootException();
+ throw new NoRootException(exceptionMessage);
else
throw new ErrnoException("Unknown error when obtaining root access", errno);
}
@@ -137,7 +141,8 @@ public 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.
*/
- public int run(final List