ErrorMessages: do not use R from backend

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2020-03-09 00:52:33 +08:00
parent 453a1aaa65
commit afd75cc4cf
6 changed files with 83 additions and 47 deletions

View File

@ -41,13 +41,6 @@ public interface Backend {
*/
Statistics getStatistics(Tunnel tunnel) throws Exception;
/**
* Determine type name of underlying backend.
*
* @return Type name
*/
String getTypePrettyName();
/**
* Determine version of underlying backend.
*

View File

@ -0,0 +1,31 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
public final class BackendException extends Exception {
public enum Reason {
UNKNOWN_KERNEL_MODULE_NAME,
WG_QUICK_CONFIG_ERROR_CODE,
TUNNEL_MISSING_CONFIG,
VPN_NOT_AUTHORIZED,
UNABLE_TO_START_VPN,
TUN_CREATION_ERROR,
GO_ACTIVATION_ERROR_CODE
}
private final Reason reason;
private final Object[] format;
public BackendException(final Reason reason, final Object ...format) {
this.reason = reason;
this.format = format;
}
public Reason getReason() {
return reason;
}
public Object[] getFormat() {
return format;
}
}

View File

@ -14,10 +14,8 @@ import androidx.annotation.Nullable;
import androidx.collection.ArraySet;
import android.util.Log;
import com.wireguard.android.Application;
import com.wireguard.android.R;
import com.wireguard.android.backend.BackendException.Reason;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.android.util.SharedLibraryLoader;
import com.wireguard.config.Config;
import com.wireguard.config.InetNetwork;
@ -28,7 +26,6 @@ import com.wireguard.crypto.KeyFormatException;
import java.net.InetAddress;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -130,11 +127,6 @@ public final class GoBackend implements Backend {
return stats;
}
@Override
public String getTypePrettyName() {
return context.getString(R.string.type_name_go_userspace);
}
@Override
public String getVersion() {
return wgVersion();
@ -171,10 +163,11 @@ public final class GoBackend implements Backend {
Log.i(TAG, "Bringing tunnel " + tunnel.getName() + " " + state);
if (state == State.UP) {
Objects.requireNonNull(config, context.getString(R.string.no_config_error));
if (config == null)
throw new BackendException(Reason.TUNNEL_MISSING_CONFIG);
if (VpnService.prepare(context) != null)
throw new Exception(context.getString(R.string.vpn_not_authorized_error));
throw new BackendException(Reason.VPN_NOT_AUTHORIZED);
final VpnService service;
if (!vpnService.isDone())
@ -183,7 +176,9 @@ public final class GoBackend implements Backend {
try {
service = vpnService.get(2, TimeUnit.SECONDS);
} catch (final TimeoutException e) {
throw new Exception(context.getString(R.string.vpn_start_error), e);
final Exception be = new BackendException(Reason.UNABLE_TO_START_VPN);
be.initCause(e);
throw be;
}
service.setOwner(this);
@ -225,12 +220,12 @@ public final class GoBackend implements Backend {
builder.setBlocking(true);
try (final ParcelFileDescriptor tun = builder.establish()) {
if (tun == null)
throw new Exception(context.getString(R.string.tun_create_error));
throw new BackendException(Reason.TUN_CREATION_ERROR);
Log.d(TAG, "Go backend v" + wgVersion());
currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig);
}
if (currentTunnelHandle < 0)
throw new Exception(context.getString(R.string.tunnel_on_error, currentTunnelHandle));
throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle);
currentTunnel = tunnel;
currentConfig = config;

View File

@ -5,12 +5,13 @@
package com.wireguard.android.backend;
import android.content.Context;
import androidx.annotation.Nullable;
import android.content.Context;
import android.util.Log;
import com.wireguard.android.Application;
import com.wireguard.android.R;
import com.wireguard.android.backend.BackendException.Reason;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.config.Config;
import com.wireguard.crypto.Key;
@ -40,13 +41,11 @@ public final class WgQuickBackend implements Backend {
private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName();
private final File localTemporaryDir;
private final Context context;
private final Map<Tunnel, Config> runningConfigs = new HashMap<>();
private final Set<TunnelStateChangeNotificationReceiver> notifiers = new HashSet<>();
public WgQuickBackend(final Context context) {
localTemporaryDir = new File(context.getCacheDir(), "tmp");
this.context = context;
}
@Override
@ -92,17 +91,12 @@ public final class WgQuickBackend implements Backend {
return stats;
}
@Override
public String getTypePrettyName() {
return context.getString(R.string.type_name_kernel_module);
}
@Override
public String getVersion() throws Exception {
final List<String> output = new ArrayList<>();
if (Application.getRootShell()
.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty())
throw new Exception(context.getString(R.string.module_version_error));
throw new BackendException(Reason.UNKNOWN_KERNEL_MODULE_NAME);
return output.get(0);
}
@ -150,7 +144,7 @@ public final class WgQuickBackend implements Backend {
// noinspection ResultOfMethodCallIgnored
tempFile.delete();
if (result != 0)
throw new Exception(context.getString(R.string.tunnel_config_error, result));
throw new BackendException(Reason.WG_QUICK_CONFIG_ERROR_CODE, result);
if (state == State.UP)
runningConfigs.put(tunnel, config);

View File

@ -16,21 +16,32 @@ import android.util.AttributeSet;
import com.wireguard.android.Application;
import com.wireguard.android.BuildConfig;
import com.wireguard.android.R;
import com.wireguard.android.backend.Backend;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.backend.WgQuickBackend;
import java.util.Locale;
public class VersionPreference extends Preference {
@Nullable private String versionSummary;
private String getBackendPrettyName(final Context context, final Backend backend) {
if (backend instanceof GoBackend)
return context.getString(R.string.type_name_kernel_module);
if (backend instanceof WgQuickBackend)
return context.getString(R.string.type_name_go_userspace);
return "";
}
public VersionPreference(final Context context, final AttributeSet attrs) {
super(context, attrs);
Application.getBackendAsync().thenAccept(backend -> {
versionSummary = getContext().getString(R.string.version_summary_checking, backend.getTypePrettyName().toLowerCase(Locale.ENGLISH));
versionSummary = getContext().getString(R.string.version_summary_checking, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH));
Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete((version, exception) -> {
versionSummary = exception == null
? getContext().getString(R.string.version_summary, backend.getTypePrettyName(), version)
: getContext().getString(R.string.version_summary_unknown, backend.getTypePrettyName().toLowerCase(Locale.ENGLISH));
? getContext().getString(R.string.version_summary, getBackendPrettyName(context, backend), version)
: getContext().getString(R.string.version_summary_unknown, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH));
notifyChanged();
});
});

View File

@ -12,9 +12,9 @@ import androidx.annotation.Nullable;
import com.wireguard.android.Application;
import com.wireguard.android.R;
import com.wireguard.android.backend.BackendException;
import com.wireguard.config.BadConfigException;
import com.wireguard.config.BadConfigException.Location;
import com.wireguard.config.BadConfigException.Reason;
import com.wireguard.config.InetEndpoint;
import com.wireguard.config.InetNetwork;
import com.wireguard.config.ParseException;
@ -29,16 +29,25 @@ import java.util.Map;
import java9.util.Maps;
public final class ErrorMessages {
private static final Map<Reason, Integer> BCE_REASON_MAP = new EnumMap<>(Maps.of(
Reason.INVALID_KEY, R.string.bad_config_reason_invalid_key,
Reason.INVALID_NUMBER, R.string.bad_config_reason_invalid_number,
Reason.INVALID_VALUE, R.string.bad_config_reason_invalid_value,
Reason.MISSING_ATTRIBUTE, R.string.bad_config_reason_missing_attribute,
Reason.MISSING_SECTION, R.string.bad_config_reason_missing_section,
Reason.MISSING_VALUE, R.string.bad_config_reason_missing_value,
Reason.SYNTAX_ERROR, R.string.bad_config_reason_syntax_error,
Reason.UNKNOWN_ATTRIBUTE, R.string.bad_config_reason_unknown_attribute,
Reason.UNKNOWN_SECTION, R.string.bad_config_reason_unknown_section
private static final Map<BadConfigException.Reason, Integer> BCE_REASON_MAP = new EnumMap<>(Maps.of(
BadConfigException.Reason.INVALID_KEY, R.string.bad_config_reason_invalid_key,
BadConfigException.Reason.INVALID_NUMBER, R.string.bad_config_reason_invalid_number,
BadConfigException.Reason.INVALID_VALUE, R.string.bad_config_reason_invalid_value,
BadConfigException.Reason.MISSING_ATTRIBUTE, R.string.bad_config_reason_missing_attribute,
BadConfigException.Reason.MISSING_SECTION, R.string.bad_config_reason_missing_section,
BadConfigException.Reason.MISSING_VALUE, R.string.bad_config_reason_missing_value,
BadConfigException.Reason.SYNTAX_ERROR, R.string.bad_config_reason_syntax_error,
BadConfigException.Reason.UNKNOWN_ATTRIBUTE, R.string.bad_config_reason_unknown_attribute,
BadConfigException.Reason.UNKNOWN_SECTION, R.string.bad_config_reason_unknown_section
));
private static final Map<BackendException.Reason, Integer> BE_REASON_MAP = new EnumMap<>(Maps.of(
BackendException.Reason.UNKNOWN_KERNEL_MODULE_NAME, R.string.module_version_error,
BackendException.Reason.WG_QUICK_CONFIG_ERROR_CODE, R.string.tunnel_config_error,
BackendException.Reason.TUNNEL_MISSING_CONFIG, R.string.no_config_error,
BackendException.Reason.VPN_NOT_AUTHORIZED, R.string.vpn_not_authorized_error,
BackendException.Reason.UNABLE_TO_START_VPN, R.string.vpn_start_error,
BackendException.Reason.TUN_CREATION_ERROR, R.string.tun_create_error,
BackendException.Reason.GO_ACTIVATION_ERROR_CODE, R.string.tunnel_on_error
));
private static final Map<Format, Integer> KFE_FORMAT_MAP = new EnumMap<>(Maps.of(
Format.BASE64, R.string.key_length_explanation_base64,
@ -77,6 +86,9 @@ public final class ErrorMessages {
bce.getLocation().getName());
final String explanation = getBadConfigExceptionExplanation(resources, bce);
message = resources.getString(R.string.bad_config_error, reason, context) + explanation;
} else if (rootCause instanceof BackendException) {
final BackendException be = (BackendException) rootCause;
message = resources.getString(BE_REASON_MAP.get(be.getReason()), be.getFormat());
} else if (rootCause.getMessage() != null) {
message = rootCause.getMessage();
} else {
@ -123,7 +135,7 @@ public final class ErrorMessages {
private static Throwable rootCause(final Throwable throwable) {
Throwable cause = throwable;
while (cause.getCause() != null) {
if (cause instanceof BadConfigException)
if (cause instanceof BadConfigException || cause instanceof BackendException)
break;
final Throwable nextCause = cause.getCause();
if (nextCause instanceof RemoteException)