diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 6db2d7dd7..ca39d6cf9 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -52,6 +52,7 @@ import net.i2p.router.startup.StartupJob; import net.i2p.router.startup.WorkingDir; import net.i2p.router.tasks.*; import net.i2p.router.transport.FIFOBandwidthLimiter; +import net.i2p.router.transport.UPnPScannerCallback; import net.i2p.router.transport.ntcp.NTCPTransport; import net.i2p.router.transport.udp.UDPTransport; import net.i2p.router.util.EventLog; @@ -103,6 +104,7 @@ public class Router implements RouterClock.ClockShiftListener { private FamilyKeyCrypto _familyKeyCrypto; private boolean _familyKeyCryptoFail; public final Object _familyKeyLock = new Object(); + private UPnPScannerCallback _upnpScannerCallback; public final static String PROP_CONFIG_FILE = "router.configLocation"; @@ -384,6 +386,8 @@ public class Router implements RouterClock.ClockShiftListener { } catch (NumberFormatException nfe) {} } _networkID = id; + // for testing + setUPnPScannerCallback(new LoggerCallback()); changeState(State.INITIALIZED); // ********* Start no threads before here ********* // } @@ -606,6 +610,32 @@ public class Router implements RouterClock.ClockShiftListener { */ public RouterContext getContext() { return _context; } + private class LoggerCallback implements UPnPScannerCallback { + public void beforeScan() { _log.info("SSDP beforeScan()"); } + public void afterScan() { _log.info("SSDP afterScan()"); } + } + + /** + * For Android only. + * MUST be set before runRouter() is called. + * + * @param callback the callback or null to clear it + * @since 0.9.41 + */ + public synchronized void setUPnPScannerCallback(UPnPScannerCallback callback) { + _upnpScannerCallback = callback; + } + + /** + * For Android only. + * + * @return the callback or null if none + * @since 0.9.41 + */ + public synchronized UPnPScannerCallback getUPnPScannerCallback() { + return _upnpScannerCallback; + } + /** * This must be called after instantiation. * Starts the threads. Does not install updates. diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index ad54f9db9..ded3ee35a 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -341,7 +341,10 @@ public class TransportManager implements TransportEventListener { // Maybe we need a config option to force on? Probably not. // What firewall supports UPnP and is configured with a public address on the LAN side? // Unlikely. - if (_upnpManager != null && Addresses.getAnyAddress() == null) + // Always start on Android, as we may have a cellular IPv4 address but + // are routing all traffic through WiFi. + // Also, conditions may change rapidly. + if (_upnpManager != null && (SystemVersion.isAndroid() || Addresses.getAnyAddress() == null)) _upnpManager.start(); configTransports(); _log.debug("Starting up the transport manager"); diff --git a/router/java/src/net/i2p/router/transport/UPnPManager.java b/router/java/src/net/i2p/router/transport/UPnPManager.java index 7802849ad..9be9570f7 100644 --- a/router/java/src/net/i2p/router/transport/UPnPManager.java +++ b/router/java/src/net/i2p/router/transport/UPnPManager.java @@ -34,6 +34,8 @@ class UPnPManager { private final RouterContext _context; private final UPnP _upnp; private final UPnPCallback _upnpCallback; + private final UPnPScannerCallback _scannerCallback; + private final DelayedCallback _delayedCallback; private volatile boolean _isRunning; private volatile boolean _shouldBeRunning; private volatile long _lastRescan; @@ -73,6 +75,8 @@ class UPnPManager { Debug.initialize(context); _upnp = new UPnP(context); _upnpCallback = new UPnPCallback(); + _scannerCallback = _context.router().getUPnPScannerCallback(); + _delayedCallback = (_scannerCallback != null) ? new DelayedCallback() : null; _rescanner = new Rescanner(); } @@ -91,7 +95,16 @@ class UPnPManager { // and will eventually hit 1024 and then negative _upnp.setHTTPPort(_context.getProperty(PROP_HTTP_PORT, DEFAULT_HTTP_PORT)); _upnp.setSSDPPort(_context.getProperty(PROP_SSDP_PORT, DEFAULT_SSDP_PORT)); + if (_scannerCallback != null) { + _scannerCallback.beforeScan(); + } _isRunning = _upnp.runPlugin(); + if (_scannerCallback != null) { + if (_isRunning) + _delayedCallback.reschedule(); + else + _scannerCallback.afterScan(); + } if (_log.shouldDebug()) _log.info("UPnP runPlugin took " + (_context.clock().now() - b)); } catch (RuntimeException e) { @@ -100,6 +113,9 @@ class UPnPManager { _log.error("UPnP error, please report", e); _errorLogged = true; } + if (_scannerCallback != null) { + _scannerCallback.afterScan(); + } } } if (_isRunning) { @@ -156,6 +172,8 @@ class UPnPManager { return false; _lastRescan = now; if (_isRunning) { + if (_scannerCallback != null) + _scannerCallback.beforeScan(); if (_log.shouldLog(Log.DEBUG)) _log.debug("UPnP Rescan"); // TODO default search MX (jitter) is 3 seconds... reduce? @@ -163,6 +181,8 @@ class UPnPManager { // Adaptive Jitter Control for UPnP M-Search // Kevin Mills and Christopher Dabrowski _upnp.search(); + if (_scannerCallback != null) + _delayedCallback.reschedule(); } else { start(); } @@ -188,6 +208,33 @@ class UPnPManager { } } } + + /** + * Delayed Callback + * + * @since 0.9.41 + */ + private class DelayedCallback extends SimpleTimer2.TimedEvent { + + /** caller must reschedule() */ + public DelayedCallback() { + super(_context.simpleTimer2()); + } + + public void timeReached() { + _scannerCallback.afterScan(); + } + + /** + * Pushes out. + * We do it this way because we may have two scans running concurrently, + * we only want to call afterScan() once. + */ + void reschedule() { + // false == use latest time + reschedule((_upnp.getSearchMx() * 1000) + 500, false); + } + } /** * Call when the ports might have changed diff --git a/router/java/src/net/i2p/router/transport/UPnPScannerCallback.java b/router/java/src/net/i2p/router/transport/UPnPScannerCallback.java new file mode 100644 index 000000000..79c3c57bf --- /dev/null +++ b/router/java/src/net/i2p/router/transport/UPnPScannerCallback.java @@ -0,0 +1,22 @@ +package net.i2p.router.transport; + +/** + * For Android MulticastLock. + * + * @since 0.9.41 + */ +public interface UPnPScannerCallback { + + /** + * Called before a SSDP search begins. + * This may be called more than once before afterScan() + * if there are multiple searches in parallel. + */ + public void beforeScan(); + + /** + * Called after a SSDP search ends. + * This will only be called once after the last scan ends. + */ + public void afterScan(); +}