diff --git a/app/src/main/java/net/i2p/android/router/MainFragment.java b/app/src/main/java/net/i2p/android/router/MainFragment.java
index 5e2740e37..36e3966bb 100644
--- a/app/src/main/java/net/i2p/android/router/MainFragment.java
+++ b/app/src/main/java/net/i2p/android/router/MainFragment.java
@@ -37,7 +37,6 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
-import net.i2p.router.CommSystemFacade;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
@@ -376,33 +375,15 @@ public class MainFragment extends I2PFragmentBase {
if (!Connectivity.isConnected(getActivity())) {
// Manually set state, RouterService won't be running
updateState(State.WAITING);
- vStatusText.setText("No Internet connection is available");
- vStatus.setVisibility(View.VISIBLE);
+ vNetworkStatus.setText(R.string.no_internet);
statusView.setVisibility(View.VISIBLE);
} else if (ctx != null) {
if (_startPressed) {
_startPressed = false;
}
- String netstatus;
- CommSystemFacade.Status reach = ctx.commSystem().getStatus();
- switch (reach) {
- case DIFFERENT:
- netstatus = "Symmetric NAT";
- break;
- case HOSED:
- netstatus = "Port Failure";
- break;
- case OK:
- netstatus = "OK";
- break;
- case REJECT_UNSOLICITED:
- netstatus = "Firewalled";
- break;
- default:
- netstatus = "Unknown";
- }
- vNetworkStatus.setText("Network: " + netstatus);
+ Util.NetStatus netStatus = Util.getNetStatus(getActivity(), ctx);
+ vNetworkStatus.setText(getString(R.string.settings_label_network) + ": " + netStatus.status);
String uptime = DataHelper.formatDuration(ctx.router().getUptime());
int active = ctx.commSystem().countActivePeers();
@@ -487,7 +468,6 @@ public class MainFragment extends I2PFragmentBase {
getActivity().findViewById(R.id.console_usage_stats).setVisibility(View.VISIBLE);
} else {
// network but no router context
- vStatusText.setText("Not running");
statusView.setVisibility(View.GONE);
getActivity().findViewById(R.id.console_usage_stats).setVisibility(View.INVISIBLE);
/**
@@ -574,6 +554,7 @@ public class MainFragment extends I2PFragmentBase {
}
}
+ private static final String SHARED_CLIENTS = "shared clients";
/**
* compare translated nicknames - put "shared clients" first in the sort
*/
@@ -583,7 +564,7 @@ public class MainFragment extends I2PFragmentBase {
public AlphaComparator(RouterContext ctx) {
_ctx = ctx;
- xsc = _(ctx, "shared clients");
+ xsc = _(ctx, SHARED_CLIENTS);
}
public int compare(Destination lhs, Destination rhs) {
@@ -617,7 +598,7 @@ public class MainFragment extends I2PFragmentBase {
}
private String _(RouterContext ctx, String s) {
- if ("shared clients".equals(s))
+ if (SHARED_CLIENTS.equals(s))
return getString(R.string.shared_clients);
else
return s;
diff --git a/app/src/main/java/net/i2p/android/router/util/Util.java b/app/src/main/java/net/i2p/android/router/util/Util.java
index de7bab9b5..17e1e5176 100644
--- a/app/src/main/java/net/i2p/android/router/util/Util.java
+++ b/app/src/main/java/net/i2p/android/router/util/Util.java
@@ -14,9 +14,13 @@ import net.i2p.android.router.I2PConstants;
import net.i2p.android.router.R;
import net.i2p.android.router.service.State;
import net.i2p.data.DataHelper;
+import net.i2p.data.router.RouterAddress;
+import net.i2p.data.router.RouterInfo;
+import net.i2p.router.CommSystemFacade;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.TransportManager;
+import net.i2p.router.transport.TransportUtil;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.util.OrderedProperties;
@@ -372,4 +376,162 @@ public abstract class Util implements I2PConstants {
state == State.MANUAL_QUITTED ||
state == State.WAITING;
}
+
+ public static class NetStatus {
+ public enum Level {
+ ERROR,
+ WARN,
+ INFO,
+ }
+
+ public final Level level;
+ public final String status;
+
+ public NetStatus(Level level, String status) {
+ this.level = level;
+ this.status = status;
+ }
+ }
+ public static NetStatus getNetStatus(Context ctx, RouterContext rCtx) {
+ if (rCtx.commSystem().isDummy())
+ return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.vm_comm_system));
+ if (rCtx.router().getUptime() > 60*1000 && (!rCtx.router().gracefulShutdownInProgress()) &&
+ !rCtx.clientManager().isAlive()) // not a router problem but the user should know
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_i2cp));
+ // Warn based on actual skew from peers, not update status, so if we successfully offset
+ // the clock, we don't complain.
+ //if (!rCtx.clock().getUpdatedSuccessfully())
+ long skew = rCtx.commSystem().getFramedAveragePeerClockSkew(33);
+ // Display the actual skew, not the offset
+ if (Math.abs(skew) > 30*1000)
+ return new NetStatus(NetStatus.Level.ERROR,
+ ctx.getString(R.string.net_status_error_skew, DataHelper.formatDuration2(Math.abs(skew))));
+ if (rCtx.router().isHidden())
+ return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.hidden));
+ RouterInfo routerInfo = rCtx.router().getRouterInfo();
+ if (routerInfo == null)
+ return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.testing));
+
+ CommSystemFacade.Status status = rCtx.commSystem().getStatus();
+ switch (status) {
+ case OK:
+ case IPV4_OK_IPV6_UNKNOWN:
+ case IPV4_OK_IPV6_FIREWALLED:
+ case IPV4_UNKNOWN_IPV6_OK:
+ case IPV4_DISABLED_IPV6_OK:
+ case IPV4_SNAT_IPV6_OK:
+ RouterAddress ra = routerInfo.getTargetAddress("NTCP");
+ if (ra == null)
+ return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
+ byte[] ip = ra.getIP();
+ if (ip == null)
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_unresolved_tcp));
+ // TODO set IPv6 arg based on configuration?
+ if (TransportUtil.isPubliclyRoutable(ip, true))
+ return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_private_tcp));
+
+ case IPV4_SNAT_IPV6_UNKNOWN:
+ case DIFFERENT:
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.symmetric_nat));
+
+ case REJECT_UNSOLICITED:
+ case IPV4_DISABLED_IPV6_FIREWALLED:
+ if (routerInfo.getTargetAddress("NTCP") != null)
+ return new NetStatus(NetStatus.Level.WARN, ctx.getString(R.string.net_status_warn_firewalled_inbound_tcp));
+ // fall through...
+ case IPV4_FIREWALLED_IPV6_OK:
+ case IPV4_FIREWALLED_IPV6_UNKNOWN:
+ if (rCtx.netDb().floodfillEnabled())
+ return new NetStatus(NetStatus.Level.WARN, ctx.getString(R.string.net_status_warn_firewalled_floodfill));
+ //if (rCtx.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
+ // return _("WARN-Firewalled and Fast");
+ return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
+
+ case DISCONNECTED:
+ return new NetStatus(NetStatus.Level.INFO, ctx.getString(R.string.net_status_info_disconnected));
+
+ case HOSED:
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_udp_port));
+
+ case UNKNOWN:
+ case IPV4_UNKNOWN_IPV6_FIREWALLED:
+ case IPV4_DISABLED_IPV6_UNKNOWN:
+ default:
+ ra = routerInfo.getTargetAddress("SSU");
+ if (ra == null && rCtx.router().getUptime() > 5*60*1000) {
+ if (rCtx.commSystem().countActivePeers() <= 0)
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_no_active_peers));
+ else if (rCtx.getProperty(ctx.getString(R.string.PROP_I2NP_NTCP_HOSTNAME)) == null ||
+ rCtx.getProperty(ctx.getString(R.string.PROP_I2NP_NTCP_PORT)) == null)
+ return new NetStatus(NetStatus.Level.ERROR, ctx.getString(R.string.net_status_error_udp_disabled_tcp_not_set));
+ else
+ return new NetStatus(NetStatus.Level.WARN, ctx.getString(R.string.net_status_warn_firewalled_udp_disabled));
+ }
+ return new NetStatus(NetStatus.Level.INFO, toStatusString(ctx, status));
+ }
+ }
+
+ private static String toStatusString(Context ctx, CommSystemFacade.Status status) {
+ String ipv4Status = "";
+ String ipv6Status = "";
+ switch (status) {
+ case OK:
+ return ctx.getString(android.R.string.ok);
+ case IPV4_OK_IPV6_UNKNOWN:
+ ipv4Status = ctx.getString(android.R.string.ok);
+ ipv6Status = ctx.getString(R.string.testing);
+ break;
+ case IPV4_OK_IPV6_FIREWALLED:
+ ipv4Status = ctx.getString(android.R.string.ok);
+ ipv6Status = ctx.getString(R.string.firewalled);
+ break;
+ case IPV4_UNKNOWN_IPV6_OK:
+ ipv4Status = ctx.getString(R.string.testing);
+ ipv6Status = ctx.getString(android.R.string.ok);
+ break;
+ case IPV4_FIREWALLED_IPV6_OK:
+ ipv4Status = ctx.getString(R.string.firewalled);
+ ipv6Status = ctx.getString(android.R.string.ok);
+ break;
+ case IPV4_DISABLED_IPV6_OK:
+ ipv4Status = ctx.getString(R.string.disabled);
+ ipv6Status = ctx.getString(android.R.string.ok);
+ break;
+ case IPV4_SNAT_IPV6_OK:
+ ipv4Status = ctx.getString(R.string.symmetric_nat);
+ ipv6Status = ctx.getString(android.R.string.ok);
+ break;
+ case DIFFERENT:
+ return ctx.getString(R.string.symmetric_nat);
+ case IPV4_SNAT_IPV6_UNKNOWN:
+ ipv4Status = ctx.getString(R.string.symmetric_nat);
+ ipv6Status = ctx.getString(R.string.testing);
+ break;
+ case IPV4_FIREWALLED_IPV6_UNKNOWN:
+ ipv4Status = ctx.getString(R.string.firewalled);
+ ipv6Status = ctx.getString(R.string.testing);
+ break;
+ case REJECT_UNSOLICITED:
+ return ctx.getString(R.string.firewalled);
+ case IPV4_UNKNOWN_IPV6_FIREWALLED:
+ ipv4Status = ctx.getString(R.string.testing);
+ ipv6Status = ctx.getString(R.string.firewalled);
+ break;
+ case IPV4_DISABLED_IPV6_UNKNOWN:
+ ipv4Status = ctx.getString(R.string.disabled);
+ ipv6Status = ctx.getString(R.string.testing);
+ break;
+ case IPV4_DISABLED_IPV6_FIREWALLED:
+ ipv4Status = ctx.getString(R.string.disabled);
+ ipv6Status = ctx.getString(R.string.firewalled);
+ break;
+ case UNKNOWN:
+ return ctx.getString(R.string.testing);
+ default:
+ return status.toStatusString();
+ }
+
+ return ctx.getString(R.string.net_status_ipv4_ipv6, ipv4Status, ipv6Status);
+ }
}
diff --git a/app/src/main/res/values/properties.xml b/app/src/main/res/values/properties.xml
index 6b91b5561..f243c9c13 100644
--- a/app/src/main/res/values/properties.xml
+++ b/app/src/main/res/values/properties.xml
@@ -11,6 +11,7 @@
i2np.udp.internalPort
+ i2np.ntcp.hostname
i2np.ntcp.autoport
i2np.ntcp.port
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f6e1b65ce..7f9d6f5dc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -30,6 +30,27 @@
Long press to stop now
Long press to cancel shutdown
+ No Internet connection is available
+
+ IPv4: %1$s; IPv6: %2$s
+ Hidden
+ Testing
+ Firewalled
+ Symmetric NAT
+
+ VM Comm System
+ Client manager I2CP error - check logs
+ Clock skew of %s
+ Unresolved TCP address
+ Private TCP address
+ Firewalled with inbound TCP enabled
+ Firewalled and floodfill
+ Disconnected - check network connection
+ UDP port in use - change in settings and restart
+ No active peers, check network connection and firewall
+ UDP disabled and inbound TCP host/port not set
+ Firewalled with UDP disabled
+
Shared clients
Uptime