diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b95b5ba16ff8b8f25cb2bf0236a6d46f896761b5..64a2c42f89bf778ee09a70b0afeab2424ddf6cb4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,6 +6,7 @@
       android:installLocation="preferExternal"
       >
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     <!-- 3 = 1.5, 2 = 1.1, 1 = 1.0; would probably work with 1 but don't have a 1.0 SDK to test against -->
     <!-- 3 required for NDK -->
diff --git a/build.properties b/build.properties
index 181724115d6453de5705a4d1fd7f7f682a345281..f620cd2fdb02b348e4a209af7cc4454296b89429 100644
--- a/build.properties
+++ b/build.properties
@@ -1 +1,3 @@
 application-package=net.i2p.router
+key.store=${user.home}/.android/${application-package}.keystore
+key.alias=${application-package}
diff --git a/build.xml b/build.xml
index 63f3d86843baf4e982ea6e1e5ee412c0dfc95d59..37710b9ca35fd2476aeec32da824a83bc3c6bf3e 100644
--- a/build.xml
+++ b/build.xml
@@ -165,6 +165,66 @@
         <delete file="scripts/version.properties" verbose="${verbose}" />
     </target>
 
+    <!-- just to make it easier -->
+    <target name="create-signing-keys" >
+        <echo message="key store is ${key.store}" />
+        <echo message="key store password is android" />
+        <echo message="key alias is ${key.alias}" />
+        <input message="Enter common name for new key (your name): " addproperty="release.cn" />
+        <fail message="You must enter a name" >
+            <condition>
+                <equals arg1="${release.cn}" arg2="" />
+            </condition>
+        </fail>
+        <input message="Enter password for new key (6 characters minimum): " addproperty="release.password" />
+        <fail message="You must enter a password" >
+            <condition>
+                <equals arg1="${release.password}" arg2="" />
+            </condition>
+        </fail>
+        <echo message="Generating keys..." />
+        <exec executable="keytool" inputstring="${release.password}${line.separator}${release.password}${line.separator}" osfamily="unix" failonerror="true">
+            <arg value="-genkey" />
+            <arg value="-v" />
+            <arg value="-alias" />
+            <arg value="${key.alias}" />
+            <arg value="-keystore" />
+            <arg value="${key.store}" />
+            <arg value="-validity" />
+            <arg value="10000" />
+            <arg value="-keyalg" />
+            <arg value="RSA" />
+            <arg value="-keysize" />
+            <arg value="4096" />
+            <arg value="-storepass" />
+            <arg value="android" />
+            <arg value="-dname" />
+            <arg value="cn=${release.cn}, ou=Apps, o=I2P, c=DE" />
+        </exec>
+        <echo message="Created keys:" />
+        <exec executable="keytool" inputstring="android${line.separator}" osfamily="unix" failonerror="true">
+            <arg value="-list" />
+            <arg value="-v" />
+            <arg value="-alias" />
+            <arg value="${key.alias}" />
+            <arg value="-keystore" />
+            <arg value="${key.store}" />
+        </exec>
+    </target>
+
+    <target name="hint" >
+        <echo message="key store password is android" />
+    </target>
+
+    <target name="verify" depends="hint, release" >
+        <exec executable="jarsigner" osfamily="unix" failonerror="true">
+            <arg value="-verify" />
+            <arg value="-verbose" />
+            <arg value="-certs" />
+            <arg value="${out.release.file}" />
+        </exec>
+    </target>
+
     <!--
          ================================================================================
          From here down copied from SDK tools/ant/main_rules.xml from Tools version 11
diff --git a/jni/build.sh b/jni/build.sh
index 28637a80c00dc484897641a87d96f7463bf9f554..15dfa5e6c753d69b17dd40b86bbe380e347f2cc9 100755
--- a/jni/build.sh
+++ b/jni/build.sh
@@ -20,11 +20,23 @@ export AABI=arm-linux-androideabi-4.4.3
 export SYSTEM=linux-x86
 export BINPREFIX=arm-linux-androideabi-
 export CC="$NDK/toolchains/$AABI/prebuilt/$SYSTEM/bin/${BINPREFIX}gcc --sysroot=$SYSROOT"
+# worked without this on 4.3.2, but 5.0.2 couldn't find it
+export NM="$NDK/toolchains/$AABI/prebuilt/$SYSTEM/bin/${BINPREFIX}nm"
 
 #echo "CC is $CC"
 
 JBIGI=$(realpath $I2PBASE/core/c/jbigi)
-GMPVER=4.3.2
+#
+# GMP Version
+#
+# prelim stats on a droid
+# java (libcrypto) 29 ms
+# 4.3.2 (jbigi) 34 ms
+# 5.0.2 (jbigi) 32 ms
+# libcrypto crashes on emulator, don't trust it
+# jbigi about 20-25% slower than java on emulator
+#
+GMPVER=5.0.2
 GMP=$JBIGI/gmp-$GMPVER
 
 if [ ! -d $GMP ]
diff --git a/src/net/i2p/android/router/activity/I2PActivityBase.java b/src/net/i2p/android/router/activity/I2PActivityBase.java
index 180f289abe10b8b08edf4c51f1026bc35f3e1b56..05d89a22b585e6e0c1c6cc100a866d3a953c42a8 100644
--- a/src/net/i2p/android/router/activity/I2PActivityBase.java
+++ b/src/net/i2p/android/router/activity/I2PActivityBase.java
@@ -49,6 +49,13 @@ public abstract class I2PActivityBase extends Activity {
         super.onPause();
     }
 
+    @Override
+    public void onSaveInstanceState(Bundle outState)
+    {
+        System.err.println(this + " onSaveInstanceState called");
+        super.onSaveInstanceState(outState);
+    }
+
     @Override
     public void onStop()
     {
diff --git a/src/net/i2p/android/router/receiver/I2PReceiver.java b/src/net/i2p/android/router/receiver/I2PReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..47e5c96059b6dd17087604e9058d971b89afdd98
--- /dev/null
+++ b/src/net/i2p/android/router/receiver/I2PReceiver.java
@@ -0,0 +1,31 @@
+package net.i2p.android.router.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+
+import net.i2p.android.router.R;
+
+public class I2PReceiver extends BroadcastReceiver {
+    private final Context _context;
+
+    public I2PReceiver(Context context) {
+        super();
+        _context = context;
+        IntentFilter intents = new IntentFilter();
+        intents.addAction(Intent.ACTION_TIME_CHANGED);
+        intents.addAction(Intent.ACTION_TIME_TICK);  // once per minute, for testing
+        intents.addAction(Intent.ACTION_SCREEN_OFF);
+        intents.addAction(Intent.ACTION_SCREEN_ON);
+        intents.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        context.registerReceiver(this, intents);
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        System.out.println("Got broadcast: " + intent);
+
+        ConnectivityManager cm = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+}
diff --git a/src/net/i2p/android/router/service/Init.java b/src/net/i2p/android/router/service/Init.java
index 8928385e3d0f8eb4d53ee027b8e6e4817c886b04..9ca0a5e0491da24ed0b1b98e80ffe1cf2c33c3cf 100644
--- a/src/net/i2p/android/router/service/Init.java
+++ b/src/net/i2p/android/router/service/Init.java
@@ -43,6 +43,7 @@ class Init {
         System.err.println("user.home" + ": " + System.getProperty("user.home"));
         System.err.println("user.name" + ": " + System.getProperty("user.name"));
         System.err.println("getFilesDir()" + ": " + myDir);
+        System.err.println("max mem" + ": " + DataHelper.formatSize(Runtime.getRuntime().maxMemory()));
         System.err.println("Package" + ": " + ctx.getPackageName());
         System.err.println("Version" + ": " + getOurVersion());
         System.err.println("MODEL" + ": " + Build.MODEL);
diff --git a/src/net/i2p/android/router/service/RouterService.java b/src/net/i2p/android/router/service/RouterService.java
index 50d7cf644a268deb4b9283602199cdad11c4fc10..34804c5833dd57062dd6361ada164b8189525145 100644
--- a/src/net/i2p/android/router/service/RouterService.java
+++ b/src/net/i2p/android/router/service/RouterService.java
@@ -6,9 +6,11 @@ import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
 
+import java.text.DecimalFormat;
 import java.util.List;
 
 import net.i2p.android.router.R;
+import net.i2p.data.DataHelper;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.RouterLaunch;
@@ -22,6 +24,7 @@ public class RouterService extends Service {
     private String _myDir;
     private int _state;
     private Thread _starterThread;
+    private Thread _statusThread;
     private StatusBar _statusBar;
     private final Object _stateLock = new Object();
 
@@ -62,8 +65,11 @@ public class RouterService extends Service {
 
     private class Starter implements Runnable {
         public void run() {
-            System.err.println(MARKER + this + " starter thread");
+            System.err.println(MARKER + this + " starter thread" +
+                           "Current state is: " + _state);
+            System.err.println(MARKER + this + " JBigI speed test started");
             NativeBigInteger.main(null);
+            System.err.println(MARKER + this + " JBigI speed test finished, launching router");
             RouterLaunch.main(null);
             synchronized (_stateLock) {
                 if (_state != STATE_STARTING)
@@ -75,6 +81,8 @@ public class RouterService extends Service {
                 _statusBar.update("I2P is running");
                 _context = (RouterContext)contexts.get(0);
                 _context.router().setKillVMOnEnd(false);
+                _statusThread = new Thread(new StatusThread());
+                _statusThread.start();
                 _context.addShutdownTask(new ShutdownHook());
                 _starterThread = null;
             }
@@ -82,6 +90,59 @@ public class RouterService extends Service {
         }
     }
 
+    private class StatusThread implements Runnable {
+        public void run() {
+            System.err.println(MARKER + this + " status thread started" +
+                               "Current state is: " + _state);
+            Router router = _context.router();
+            while (_state == STATE_RUNNING && router.isAlive()) {
+                try {
+                    Thread.sleep(5000);
+                } catch (InterruptedException ie) {
+                    break;
+                }
+                int active = _context.commSystem().countActivePeers();
+                int known = Math.max(_context.netDb().getKnownRouters() - 1, 0);
+                int inEx = _context.tunnelManager().getFreeTunnelCount();
+                int outEx = _context.tunnelManager().getOutboundTunnelCount();
+                int inCl = _context.tunnelManager().getInboundClientTunnelCount();
+                int outCl = _context.tunnelManager().getOutboundClientTunnelCount();
+                //int part = _context.tunnelManager().getParticipatingCount();
+                double dLag = _context.statManager().getRate("jobQueue.jobLag").getRate(60000).getAverageValue();
+                String jobLag = DataHelper.formatDuration((long) dLag);
+                String msgDelay = DataHelper.formatDuration(_context.throttle().getMessageDelay());
+                String uptime = DataHelper.formatDuration(router.getUptime());
+                //String tunnelStatus = _context.throttle().getTunnelStatus();
+                double inBW = _context.bandwidthLimiter().getReceiveBps() / 1024;
+                double outBW = _context.bandwidthLimiter().getSendBps() / 1024;
+                // control total width
+                DecimalFormat fmt;
+                if (inBW >= 1000 || outBW >= 1000)
+                    fmt = new DecimalFormat("#0");
+                else if (inBW >= 100 || outBW >= 100)
+                    fmt = new DecimalFormat("#0.0");
+                else
+                    fmt = new DecimalFormat("#0.00");
+
+                String status =
+                       " Pr " + active + '/' + known +
+                       " Ex " + inEx + '/' + outEx +
+                       " Cl " + inCl + '/' + outCl +
+                       //" Pt " + part +
+                       " BW " + fmt.format(inBW) + '/' + fmt.format(outBW) + "K" +
+                       " Lg " + jobLag +
+                       " Dy " + msgDelay +
+                       " Up " + uptime;
+
+                System.out.println(status);
+                _statusBar.update(status);
+            }
+            _statusBar.update("Status thread died");
+            System.err.println(MARKER + this + " status thread finished" +
+                               "Current state is: " + _state);
+        }
+    }
+
     @Override
     public IBinder onBind(Intent intent)
     {
@@ -111,7 +172,8 @@ public class RouterService extends Service {
 
     private class Stopper implements Runnable {
         public void run() {
-            System.err.println(MARKER + this + " stopper thread");
+            System.err.println(MARKER + this + " stopper thread" +
+                               "Current state is: " + _state);
             _context.router().shutdown(Router.EXIT_HARD);
             _statusBar.off(RouterService.this);
             System.err.println("shutdown complete");
@@ -128,6 +190,8 @@ public class RouterService extends Service {
             synchronized (_stateLock) {
                 if (_state == STATE_STARTING || _state == STATE_RUNNING) {
                     _state = STATE_STOPPED;
+                    if (_statusThread != null)
+                        _statusThread.interrupt();
                     _statusBar.off(RouterService.this);
                     stopSelf();
                 }