diff --git a/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java b/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java
index 9e70ee5e146d116d07db11f0fb27a41a9c15dc62..e80aa8c74e06b94371e144112743dd6a1f09883a 100644
--- a/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java
+++ b/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java
@@ -37,6 +37,7 @@ import java.util.Map;
 
 import net.i2p.util.SecureFile;
 import net.i2p.util.SecureFileOutputStream;
+import net.i2p.util.SystemVersion;
 
 /**
  * Utility class providing methods to parse and write files in config file
@@ -49,7 +50,7 @@ import net.i2p.util.SecureFileOutputStream;
  */
 class ConfigParser {
 
-    private static final boolean isWindows = System.getProperty("os.name").startsWith("Win");
+    private static final boolean isWindows = SystemVersion.isWindows();
 
     /**
      * Strip the comments from a String. Lines that begin with '#' and ';' are
diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
index 145a76a28ca51d81d8094ec50a04f31d332dca2a..b343f3735958dac71ffab69eedb7ac83b656f202 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java
@@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import net.i2p.crypto.SHA1;
 import net.i2p.util.Log;
 import net.i2p.util.SecureFile;
+import net.i2p.util.SystemVersion;
 
 /**
  * Maintains pieces on disk. Can be used to store and retrieve pieces.
@@ -77,7 +78,7 @@ public class Storage
 
   private static final Map<String, String> _filterNameCache = new ConcurrentHashMap();
 
-  private static final boolean _isWindows = System.getProperty("os.name").startsWith("Win");
+  private static final boolean _isWindows = SystemVersion.isWindows();
 
   /**
    * Creates a new storage based on the supplied MetaInfo.  This will
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java
index a9222158d6562682105455ee5c155ecf4c72ae32..6b79c40617f8bb381c00dadfe7d37481fefece23 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java
@@ -8,6 +8,7 @@ import net.i2p.apps.systray.UrlLauncher;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.startup.ClientAppConfig;
+import net.i2p.util.SystemVersion;
 import net.i2p.util.VersionComparator;
 
 import org.tanukisoftware.wrapper.WrapperManager;
@@ -140,7 +141,7 @@ public class ConfigServiceHandler extends FormHandler {
      */
     synchronized static void registerSignalHandler(RouterContext ctx) {
         if (ctx.hasWrapper() && _wrapperListener == null &&
-            !System.getProperty("os.name").startsWith("Win")) {
+            !SystemVersion.isWindows()) {
             String wv = System.getProperty("wrapper.version");
             if (wv != null && (new VersionComparator()).compare(wv, LISTENER_AVAILABLE) >= 0) {
                 try {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java
index abea0f898089fddf3ada086deb931d4d513aeab4..825133c0eeb4cb3bb8d89f513519d53f281fb5f0 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/StatSummarizer.java
@@ -23,6 +23,7 @@ import net.i2p.stat.Rate;
 import net.i2p.stat.RateStat;
 import net.i2p.util.FileUtil;
 import net.i2p.util.Log;
+import net.i2p.util.SystemVersion;
 
 import org.jrobin.core.RrdException;
 import org.jrobin.graph.RrdGraph;
@@ -418,7 +419,7 @@ public class StatSummarizer implements Runnable {
         FileUtil.rmdir(rrdDir, false);
     }
 
-    private static final boolean IS_WIN = System.getProperty("os.name").startsWith("Win");
+    private static final boolean IS_WIN = SystemVersion.isWindows();
 
     /** translate a string */
     private String _(String s) {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java
index 494e93de8355b550048ddde138b88eec877b2214..bea95b7978a716c2becbff5ebd56698b5f20c76b 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryRenderer.java
@@ -18,6 +18,7 @@ import net.i2p.data.DataHelper;
 import net.i2p.router.RouterContext;
 import net.i2p.router.util.EventLog;
 import net.i2p.util.Log;
+import net.i2p.util.SystemVersion;
 
 import org.jrobin.core.RrdException;
 import org.jrobin.graph.RrdGraph;
@@ -221,7 +222,7 @@ class SummaryRenderer {
         }
     }
 
-    private static final boolean IS_WIN = System.getProperty("os.name").startsWith("Win");
+    private static final boolean IS_WIN = SystemVersion.isWindows();
 
     /** translate a string */
     private String _(String s) {
diff --git a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java
index 233f46c5bfd8cfdc6cdfadc6ecb7f4c2d8245abc..f966533f3ca5df36ec83a8e539196e030d02e5a6 100644
--- a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java
+++ b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java
@@ -15,6 +15,7 @@ import java.io.File;
 import net.i2p.I2PAppContext;
 import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SystemVersion;
 
 import snoozesoft.systray4j.SysTrayMenu;
 import snoozesoft.systray4j.SysTrayMenuEvent;
@@ -37,8 +38,7 @@ public class SysTray implements SysTrayMenuListener {
     private static String         _portString;
     private static boolean        _showIcon;
     private static UrlLauncher    _urlLauncher    = new UrlLauncher();
-    private static final boolean _is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
-                                         System.getProperty("os.arch").contains("64");
+    private static final boolean _is64 = SystemVersion.is64Bit();
 
     static {
         File config = new File(I2PAppContext.getGlobalContext().getConfigDir(), "systray.config");
@@ -54,7 +54,7 @@ public class SysTray implements SysTrayMenuListener {
         //if (!(new File("router.config")).exists())
         //    openRouterConsole("http://localhost:" + _portString + "/index.jsp");
 
-        if ( (System.getProperty("os.name").startsWith("Windows")) && (!Boolean.getBoolean("systray.disable")) && (!_is64))
+        if ( (SystemVersion.isWindows()) && (!Boolean.getBoolean("systray.disable")) && (!_is64))
             _instance = new SysTray();
     }
 
diff --git a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java
index 2a1589e11cb1ec27b6e6f17559f0b6222165d162..f93f0876a2cb2479484d33403cc6eeddfa8fa2ea 100644
--- a/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java
+++ b/apps/systray/java/src/net/i2p/apps/systray/UrlLauncher.java
@@ -23,6 +23,7 @@ import java.util.Locale;
 
 import net.i2p.I2PAppContext;
 import net.i2p.util.ShellCommand;
+import net.i2p.util.SystemVersion;
 
 /**
  * A quick and simple multi-platform URL launcher. It attempts to launch the
@@ -124,7 +125,7 @@ public class UrlLauncher {
                 if (_shellCommand.executeSilentAndWaitTimed("iexplore " + url, 5))
                     return true;
 
-            } else if (osName.startsWith("Windows")) {
+            } else if (SystemVersion.isWindows()) {
 
                 String         browserString  = "\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome";
                 BufferedReader bufferedReader = null;
diff --git a/core/java/src/freenet/support/CPUInformation/CPUID.java b/core/java/src/freenet/support/CPUInformation/CPUID.java
index b5240a0b7f1027fc16b0615b1b937928f41f235b..2c59dcfd1fa4f52df49128cbfbe42af3328e27f4 100644
--- a/core/java/src/freenet/support/CPUInformation/CPUID.java
+++ b/core/java/src/freenet/support/CPUInformation/CPUID.java
@@ -19,6 +19,7 @@ import java.util.Locale;
 
 import net.i2p.I2PAppContext;
 import net.i2p.util.FileUtil;
+import net.i2p.util.SystemVersion;
 
 
 /**
@@ -50,15 +51,15 @@ public class CPUID {
 
     private static final boolean isX86 = System.getProperty("os.arch").contains("86") ||
                                          System.getProperty("os.arch").equals("amd64");
-    private static final String libPrefix = (System.getProperty("os.name").startsWith("Win") ? "" : "lib");
-    private static final String libSuffix = (System.getProperty("os.name").startsWith("Win") ? ".dll" : ".so");
-    private static final boolean isWindows = System.getProperty("os.name").toLowerCase(Locale.US).contains("windows");
+    private static final boolean isWindows = SystemVersion.isWindows();
+    private static final String libPrefix = isWindows ? "" : "lib";
+    private static final String libSuffix = isWindows ? ".dll" : ".so";
     private static final boolean isLinux = System.getProperty("os.name").toLowerCase(Locale.US).contains("linux");
     private static final boolean isFreebsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("freebsd");
     private static final boolean isNetbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("netbsd");
     private static final boolean isOpenbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("openbsd");
     private static final boolean isSunos = System.getProperty("os.name").toLowerCase(Locale.US).contains("sunos");
-    private static final boolean isMac = System.getProperty("os.name").startsWith("Mac");
+    private static final boolean isMac = SystemVersion.isMac();
 
 
     /**
@@ -71,8 +72,7 @@ public class CPUID {
      * sun.arch.data.model == 32 => A 32 bit JVM but could be either 32 or 64 bit processor or libs
      * os.arch contains "64" could be 32 or 64 bit libs
      */
-    private static final boolean is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
-                                        System.getProperty("os.arch").contains("64");
+    private static final boolean is64 = SystemVersion.is64Bit();
 
     static
     {
diff --git a/core/java/src/net/i2p/util/BigPipedInputStream.java b/core/java/src/net/i2p/util/BigPipedInputStream.java
index 56555ad88040c8e96fe6be47e909516e41c84f0a..8381c7d212910cd9a9605a0b337129dec5060ae1 100644
--- a/core/java/src/net/i2p/util/BigPipedInputStream.java
+++ b/core/java/src/net/i2p/util/BigPipedInputStream.java
@@ -15,8 +15,7 @@ import java.io.PipedInputStream;
  */
 public class BigPipedInputStream extends PipedInputStream {
 
-    private static final boolean oneDotSix =
-        (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
+    private static final boolean oneDotSix = SystemVersion.isJava6();
 
     private static final int PIPE_SIZE = 64*1024;
 
diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java
index 8e6d874ba8aa5a8ff4654d19304ca5d9ba933a66..c29422c10d43a7d2ab57d174f20d5215284f7085 100644
--- a/core/java/src/net/i2p/util/FileUtil.java
+++ b/core/java/src/net/i2p/util/FileUtil.java
@@ -452,7 +452,7 @@ public class FileUtil {
         if (!from.exists())
             return false;
         boolean success = false;
-        boolean isWindows = System.getProperty("os.name").startsWith("Win");
+        boolean isWindows = SystemVersion.isWindows();
         // overwrite fails on windows
         if (!isWindows)
             success = from.renameTo(to);
diff --git a/core/java/src/net/i2p/util/NativeBigInteger.java b/core/java/src/net/i2p/util/NativeBigInteger.java
index 43476aa34bbc917a3bf56266b2ab11c7265d8c1b..e1a5c0bbc4c3113bb9018d546cae46ed003d3d58 100644
--- a/core/java/src/net/i2p/util/NativeBigInteger.java
+++ b/core/java/src/net/i2p/util/NativeBigInteger.java
@@ -158,15 +158,15 @@ public class NativeBigInteger extends BigInteger {
     /**
      * Operating systems
      */
-    private static final boolean _isWin = System.getProperty("os.name").startsWith("Win");
+    private static final boolean _isWin = SystemVersion.isWindows();
     private static final boolean _isOS2 = System.getProperty("os.name").startsWith("OS/2");
-    private static final boolean _isMac = System.getProperty("os.name").startsWith("Mac");
+    private static final boolean _isMac = SystemVersion.isMac();
     private static final boolean _isLinux = System.getProperty("os.name").toLowerCase(Locale.US).contains("linux");
     private static final boolean _isFreebsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("freebsd");
     private static final boolean _isNetbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("netbsd");
     private static final boolean _isOpenbsd = System.getProperty("os.name").toLowerCase(Locale.US).contains("openbsd");
     private static final boolean _isSunos = System.getProperty("os.name").toLowerCase(Locale.US).contains("sunos");
-    private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android");
+    private static final boolean _isAndroid = SystemVersion.isAndroid();
 
     /*
      * This isn't always correct.
@@ -178,8 +178,7 @@ public class NativeBigInteger extends BigInteger {
      * sun.arch.data.model == 32 => A 32 bit JVM but could be either 32 or 64 bit processor or libs
      * os.arch contains "64" could be 32 or 64 bit libs
      */
-    private static final boolean _is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
-                                         System.getProperty("os.arch").contains("64");
+    private static final boolean _is64 = SystemVersion.is64Bit();
 
     private static final boolean _isX86 = System.getProperty("os.arch").contains("86") ||
 	                                 System.getProperty("os.arch").equals("amd64");
diff --git a/core/java/src/net/i2p/util/ReusableGZIPInputStream.java b/core/java/src/net/i2p/util/ReusableGZIPInputStream.java
index 0528c8591bea3e8ad87b861d43d240e7d4f5889a..f09c9711c3e4c1c84bc984d42c5ecee0bf99bcae 100644
--- a/core/java/src/net/i2p/util/ReusableGZIPInputStream.java
+++ b/core/java/src/net/i2p/util/ReusableGZIPInputStream.java
@@ -10,7 +10,7 @@ public class ReusableGZIPInputStream extends ResettableGZIPInputStream {
     // Apache Harmony 5.0M13 Deflater doesn't work after reset()
     // Neither does Android
     private static final boolean ENABLE_CACHING = !(System.getProperty("java.vendor").startsWith("Apache") ||
-                                                    System.getProperty("java.vendor").contains("Android"));
+                                                    SystemVersion.isAndroid());
     private static final LinkedBlockingQueue<ReusableGZIPInputStream> _available;
     static {
         if (ENABLE_CACHING)
diff --git a/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java b/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java
index ab1d6ec4364be07d4baa36c93b8efa9a79d0ae99..fe686095e440e54556ff9dbcd7a44e76ee363f44 100644
--- a/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java
+++ b/core/java/src/net/i2p/util/ReusableGZIPOutputStream.java
@@ -21,7 +21,7 @@ public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
     // Apache Harmony 5.0M13 Deflater doesn't work after reset()
     // Neither does Android
     private static final boolean ENABLE_CACHING = !(System.getProperty("java.vendor").startsWith("Apache") ||
-                                                    System.getProperty("java.vendor").contains("Android"));
+                                                    SystemVersion.isAndroid());
     private static final LinkedBlockingQueue<ReusableGZIPOutputStream> _available;
     static {
         if (ENABLE_CACHING)
diff --git a/core/java/src/net/i2p/util/SSLEepGet.java b/core/java/src/net/i2p/util/SSLEepGet.java
index 7187ec91bed605e4dd3008330e0d39a3428c8022..8958a466e7e3ce9b6dabeac529015323bc6173e7 100644
--- a/core/java/src/net/i2p/util/SSLEepGet.java
+++ b/core/java/src/net/i2p/util/SSLEepGet.java
@@ -91,7 +91,7 @@ public class SSLEepGet extends EepGet {
     /** may be null if init failed */
     private SavingTrustManager _stm;
 
-    private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android");
+    private static final boolean _isAndroid = SystemVersion.isAndroid();
 
     /**
      *  A new SSLEepGet with a new SSLState
diff --git a/core/java/src/net/i2p/util/SecureDirectory.java b/core/java/src/net/i2p/util/SecureDirectory.java
index d238cc694d18cad02704950afabe0ba60117c500..b94a13ffda9fa90973696789a9cfe360e1fbad88 100644
--- a/core/java/src/net/i2p/util/SecureDirectory.java
+++ b/core/java/src/net/i2p/util/SecureDirectory.java
@@ -12,7 +12,7 @@ import java.io.File;
  */
 public class SecureDirectory extends File {
 
-    protected static final boolean isNotWindows = !System.getProperty("os.name").startsWith("Win");
+    protected static final boolean isNotWindows = !SystemVersion.isWindows();
 
     public SecureDirectory(String pathname) {
         super(pathname);
diff --git a/core/java/src/net/i2p/util/SecureFileOutputStream.java b/core/java/src/net/i2p/util/SecureFileOutputStream.java
index 9494827eb6fd8a718a2c5096b55b10ed1c157219..41ac5f865c13af20919d64d3f037e20629669f27 100644
--- a/core/java/src/net/i2p/util/SecureFileOutputStream.java
+++ b/core/java/src/net/i2p/util/SecureFileOutputStream.java
@@ -15,8 +15,7 @@ import net.i2p.I2PAppContext;
  */
 public class SecureFileOutputStream extends FileOutputStream {
 
-    private static final boolean oneDotSix =
-        (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
+    private static final boolean oneDotSix = SystemVersion.isJava6();
 
     /**
      *  Sets output file to mode 600
diff --git a/core/java/src/net/i2p/util/SystemVersion.java b/core/java/src/net/i2p/util/SystemVersion.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e965b23fb0d3d81c6f17c13262081c62c20ff8a
--- /dev/null
+++ b/core/java/src/net/i2p/util/SystemVersion.java
@@ -0,0 +1,87 @@
+package net.i2p.util;
+
+/*
+ * public domain
+ */
+
+import java.lang.reflect.Field;
+
+/**
+ * Methods to find out what system we are running on
+ *
+ * @since 0.9.3 consolidated from various places
+ */
+public abstract class SystemVersion {
+
+    private static final boolean _isWin = System.getProperty("os.name").startsWith("Win");
+    private static final boolean _isMac = System.getProperty("os.name").startsWith("Mac");
+    private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android");
+    private static final boolean _is64 = "64".equals(System.getProperty("sun.arch.data.model")) ||
+                                         System.getProperty("os.arch").contains("64");
+
+    private static final boolean _oneDotSix;
+    private static final int _androidSDK;
+
+    static {
+        int sdk = 0;
+        if (_isAndroid) {
+            try {
+                Class ver = Class.forName("android.os.Build.VERSION", true, ClassLoader.getSystemClassLoader());
+                Field field = ver.getField("SDK_INT");
+                sdk = field.getInt(null);
+            } catch (Exception e) {}
+        }
+        _androidSDK = sdk;
+
+        if (_isAndroid) {
+            _oneDotSix = _androidSDK >= 9;
+        } else {
+            _oneDotSix = (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
+        }
+    }
+
+    public static boolean isWindows() {
+        return _isWin;
+    }
+
+    public static boolean isMac() {
+        return _isMac;
+    }
+
+    public static boolean isAndroid() {
+        return _isAndroid;
+    }
+
+    /**
+     *  Better than (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0
+     *  as it handles Android also, where java.version = "0".
+     *
+     *  @return true if Java 1.6 or higher, or Android API 9 or higher
+     */
+    public static boolean isJava6() {
+        return _oneDotSix;
+    }
+
+    /**
+     * This isn't always correct.
+     * http://stackoverflow.com/questions/807263/how-do-i-detect-which-kind-of-jre-is-installed-32bit-vs-64bit
+     * http://mark.koli.ch/2009/10/javas-osarch-system-property-is-the-bitness-of-the-jre-not-the-operating-system.html
+     * http://mark.koli.ch/2009/10/reliably-checking-os-bitness-32-or-64-bit-on-windows-with-a-tiny-c-app.html
+     * sun.arch.data.model not on all JVMs
+     * sun.arch.data.model == 64 => 64 bit processor
+     * sun.arch.data.model == 32 => A 32 bit JVM but could be either 32 or 64 bit processor or libs
+     * os.arch contains "64" could be 32 or 64 bit libs
+     */
+    public static boolean is64Bit() {
+        return _is64;
+    }
+
+    /**
+     *  Identical to android.os.Build.VERSION.SDK_INT.
+     *  For use outside of Android code.
+     *  @return The SDK (API) version, e.g. 8 for Froyo, 0 if unknown
+     */
+    public static int getAndroidVersion() {
+        return _androidSDK;
+    }
+}
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 03f2791c2f3be2b2fe5bea5a0485f6621990fb67..9e5e3380c6dde4c00fc2d84aa1a5568a1e2e43b3 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.util.Log;
 import net.i2p.util.SecureFileOutputStream;
 import net.i2p.util.SimpleByteCache;
 import net.i2p.util.SimpleScheduler;
+import net.i2p.util.SystemVersion;
 
 /**
  * Main driver for the router.
@@ -207,7 +208,7 @@ public class Router implements RouterClock.ClockShiftListener {
         List<RouterContext> contexts = RouterContext.getContexts();
         if (contexts.isEmpty()) {
             RouterContext.killGlobalContext();
-        } else if (System.getProperty("java.vendor").contains("Android")) {
+        } else if (SystemVersion.isAndroid()) {
             System.err.println("Warning: Killing " + contexts.size() + " other routers in this JVM");
             contexts.clear();
             RouterContext.killGlobalContext();
@@ -260,7 +261,7 @@ public class Router implements RouterClock.ClockShiftListener {
         // *********  Start no threads before here ********* //
         //
         // NOW we can start the ping file thread.
-        if (!System.getProperty("java.vendor").contains("Android"))
+        if (!SystemVersion.isAndroid())
             beginMarkingLiveliness();
 
         // Apps may use this as an easy way to determine if they are in the router JVM
@@ -888,7 +889,7 @@ public class Router implements RouterClock.ClockShiftListener {
             //Runtime.getRuntime().halt(exitCode);
             // allow the Runtime shutdown hooks to execute
             Runtime.getRuntime().exit(exitCode);
-        } else if (System.getProperty("java.vendor").contains("Android")) {
+        } else if (SystemVersion.isAndroid()) {
             Runtime.getRuntime().gc();
         }
     }
@@ -1217,8 +1218,8 @@ public class Router implements RouterClock.ClockShiftListener {
             String osArch = System.getProperty("os.arch");
             boolean isX86 = osArch.contains("86") || osArch.equals("amd64");
             String osName = System.getProperty("os.name").toLowerCase(Locale.US);
-            boolean isWin = osName.startsWith("win");
-            boolean isMac = osName.startsWith("mac");
+            boolean isWin = SystemVersion.isWindows();
+            boolean isMac = SystemVersion.isMac();
             // only do this on these OSes
             boolean goodOS = isWin || isMac ||
                              osName.contains("linux") || osName.contains("freebsd");
diff --git a/router/java/src/net/i2p/router/startup/StartupJob.java b/router/java/src/net/i2p/router/startup/StartupJob.java
index ec7f47dc6ce975083c41e1ee306a711bf6220407..b6aa04ef0ef6f5289013c1ba3d67e09a00ec2d95 100644
--- a/router/java/src/net/i2p/router/startup/StartupJob.java
+++ b/router/java/src/net/i2p/router/startup/StartupJob.java
@@ -11,6 +11,7 @@ package net.i2p.router.startup;
 
 import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
+import net.i2p.util.SystemVersion;
 
 /**
  * The StartupJob should be run once on router startup to initialize the system
@@ -33,7 +34,7 @@ public class StartupJob extends JobImpl {
 
     public String getName() { return "Startup Router"; }
     public void runJob() {
-        if (!System.getProperty("java.vendor").contains("Android"))
+        if (!SystemVersion.isAndroid())
             getContext().jobQueue().addJob(new LoadClientAppsJob(getContext()));
         getContext().statPublisher().startup();
         getContext().jobQueue().addJob(new LoadRouterInfoJob(getContext()));
diff --git a/router/java/src/net/i2p/router/startup/WorkingDir.java b/router/java/src/net/i2p/router/startup/WorkingDir.java
index 613f3cee127ec47525669c5c5a1c7f78e2099e5c..cdec547dfeb1ba888d3d2caacdd359cefb67a2ac 100644
--- a/router/java/src/net/i2p/router/startup/WorkingDir.java
+++ b/router/java/src/net/i2p/router/startup/WorkingDir.java
@@ -13,6 +13,7 @@ import java.util.Properties;
 import net.i2p.data.DataHelper;
 import net.i2p.util.SecureDirectory;
 import net.i2p.util.SecureFileOutputStream;
+import net.i2p.util.SystemVersion;
 
 /**
  * Get a working directory for i2p.
@@ -69,7 +70,7 @@ public class WorkingDir {
             dir = envProps.getProperty(PROP_WORKING_DIR);
         if (dir == null)
             dir = System.getProperty(PROP_WORKING_DIR);
-        boolean isWindows = System.getProperty("os.name").startsWith("Win");
+        boolean isWindows = SystemVersion.isWindows();
         File dirf = null;
         if (dir != null) {
             dirf = new SecureDirectory(dir);
diff --git a/router/java/src/net/i2p/router/tasks/RouterWatchdog.java b/router/java/src/net/i2p/router/tasks/RouterWatchdog.java
index 213721f9f2eddfadeb2efd6dfb2876881a855924..2cf889162496cd86bd673089de909b9ff418a4db 100644
--- a/router/java/src/net/i2p/router/tasks/RouterWatchdog.java
+++ b/router/java/src/net/i2p/router/tasks/RouterWatchdog.java
@@ -11,6 +11,7 @@ import net.i2p.stat.Rate;
 import net.i2p.stat.RateStat;
 import net.i2p.util.ShellCommand;
 import net.i2p.util.Log;
+import net.i2p.util.SystemVersion;
 
 /**
  * Periodically check to make sure things haven't gone totally haywire (and if
@@ -112,7 +113,7 @@ public class RouterWatchdog implements Runnable {
                 // This works on linux...
                 // It won't on windows, and we can't call i2prouter.bat either, it does something
                 // completely different...
-                if (_context.hasWrapper() && !System.getProperty("os.name").startsWith("Win")) {
+                if (_context.hasWrapper() && !SystemVersion.isWindows()) {
                     ShellCommand sc = new ShellCommand();
                     File i2pr = new File(_context.getBaseDir(), "i2prouter");
                     String[] args = new String[2];
diff --git a/router/java/src/net/i2p/router/transport/udp/MTU.java b/router/java/src/net/i2p/router/transport/udp/MTU.java
index c6c8e6e79a52cf0d116606250042511135be2ff4..9258043f6b907c05add34010c8ce0e84e4b6fb98 100644
--- a/router/java/src/net/i2p/router/transport/udp/MTU.java
+++ b/router/java/src/net/i2p/router/transport/udp/MTU.java
@@ -5,7 +5,7 @@ import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.Enumeration;
 
-import net.i2p.util.VersionComparator;
+import net.i2p.util.SystemVersion;
 
 /**
  * Get the MTU for the network interface of an address.
@@ -14,8 +14,7 @@ import net.i2p.util.VersionComparator;
  */
 abstract class MTU {
 
-    private static final boolean hasMTU =
-        (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
+    private static final boolean hasMTU = SystemVersion.isJava6();
     
     /**
      * The MTU for the socket interface, if available.
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
index 09fb43822c787e5facf8691cde4b0aedc9a6183d..4daed3dfd197053476c905bf867809b7c409dd11 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
@@ -11,6 +11,7 @@ import net.i2p.router.util.CoDelBlockingQueue;
 import net.i2p.util.I2PThread;
 import net.i2p.util.Log;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SystemVersion;
 
 /**
  * Lowest level component to pull raw UDP datagrams off the wire as fast
@@ -32,7 +33,7 @@ class UDPReceiver {
     private static int __id;
     private final int _id;
 
-    private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android");
+    private static final boolean _isAndroid = SystemVersion.isAndroid();
 
     private static final int TYPE_POISON = -99999;
     private static final int MIN_QUEUE_SIZE = 16;
diff --git a/router/java/src/net/i2p/router/util/RandomIterator.java b/router/java/src/net/i2p/router/util/RandomIterator.java
index 87ca7c61066519771e96dfcca7b041068f3a950f..226c6e0009d043c178fb0159ea530d1e417189d0 100644
--- a/router/java/src/net/i2p/router/util/RandomIterator.java
+++ b/router/java/src/net/i2p/router/util/RandomIterator.java
@@ -14,6 +14,7 @@ import java.util.NoSuchElementException;
 import java.util.Random;
 
 import net.i2p.util.RandomSource;
+import net.i2p.util.SystemVersion;
 
 /**
  *
@@ -89,11 +90,17 @@ public class RandomIterator<E> implements Iterator<E> {
     /** Used to narrow the range to take random indexes from */
     private int lower, upper;
 
-    private static final boolean isAndroid = System.getProperty("java.vendor").contains("Android");
-
+    private static final boolean hasAndroidBug;
     static {
-        if (isAndroid)
-            testAndroid();
+        if (SystemVersion.isAndroid()) {
+            // only present on Gingerbread (API 11), but set if version check failed also
+            int ver = SystemVersion.getAndroidVersion();
+            hasAndroidBug = ver == 11 || ver == 0;
+            if (hasAndroidBug)
+                testAndroid();
+        } else {
+            hasAndroidBug = false;
+        }
     }
 
     public RandomIterator(List<E> list){
@@ -137,7 +144,7 @@ public class RandomIterator<E> implements Iterator<E> {
         if (hasNext()) {
             if (index == lower)
                 // workaround for Android ICS bug - see below
-                lower = isAndroid ? nextClearBit(index) : served.nextClearBit(index);
+                lower = hasAndroidBug ? nextClearBit(index) : served.nextClearBit(index);
             else if (index == upper)
                 upper = previousClearBit(index - 1);
         }
@@ -199,7 +206,7 @@ public class RandomIterator<E> implements Iterator<E> {
      *  @since 0.9.2
      */
     private static void testAndroid() {
-        System.out.println("checking for Android bug");
+        System.out.println("Checking for Android BitSet bug");
         BitSet theBitSet = new BitSet(864);
         for (int exp =0; exp < 864; exp++) {
             int act = theBitSet.nextClearBit(0);