From a9a3fbd5da83ad248121e3c05cdfe9bfa93d9420 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sat, 11 Jun 2011 20:08:53 +0000 Subject: [PATCH] - Start clients directly (hard coded) instead of using clients.config and LoadClientAppsJob, due to ClassLoader issues and the need to register shutdown hooks anyway. - Status bar tweaks - Fix NPE when stopping - Instantiate broadcast receiver - apk config has preference over installed config for now --- res/raw/clients_config | 5 -- .../android/router/receiver/I2PReceiver.java | 38 +++++++++- src/net/i2p/android/router/service/Init.java | 13 ++-- .../router/service/LoadClientsJob.java | 70 +++++++++++++++++++ .../android/router/service/RouterService.java | 48 +++++++++---- .../i2p/android/router/service/StatusBar.java | 4 ++ 6 files changed, 151 insertions(+), 27 deletions(-) delete mode 100644 res/raw/clients_config create mode 100644 src/net/i2p/android/router/service/LoadClientsJob.java diff --git a/res/raw/clients_config b/res/raw/clients_config deleted file mode 100644 index 0cf3bc988..000000000 --- a/res/raw/clients_config +++ /dev/null @@ -1,5 +0,0 @@ -# poke the i2ptunnels defined in i2ptunnel.config -clientApp.0.main=net.i2p.i2ptunnel.TunnelControllerGroup -clientApp.0.name=Application tunnels -clientApp.0.args=i2ptunnel.config -clientApp.0.startOnLoad=true diff --git a/src/net/i2p/android/router/receiver/I2PReceiver.java b/src/net/i2p/android/router/receiver/I2PReceiver.java index b2b8fcb8e..1116cf775 100644 --- a/src/net/i2p/android/router/receiver/I2PReceiver.java +++ b/src/net/i2p/android/router/receiver/I2PReceiver.java @@ -12,9 +12,13 @@ import net.i2p.android.router.R; public class I2PReceiver extends BroadcastReceiver { private final Context _context; + /** + * Registers itself + */ public I2PReceiver(Context context) { super(); _context = context; + getInfo(); IntentFilter intents = new IntentFilter(); intents.addAction(Intent.ACTION_TIME_CHANGED); intents.addAction(Intent.ACTION_TIME_TICK); // once per minute, for testing @@ -26,7 +30,7 @@ public class I2PReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - System.out.println("Got broadcast: " + action); + System.err.println("Got broadcast: " + action); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { boolean failover = intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false); @@ -34,9 +38,37 @@ public class I2PReceiver extends BroadcastReceiver { NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); NetworkInfo other = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO); - System.out.println("No conn? " + noConn + " failover? " + failover + + System.err.println("No conn? " + noConn + " failover? " + failover + " info: " + info + " other: " + other); - //ConnectivityManager cm = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE); + printInfo(info); + printInfo(other); + getInfo(); } } + + private NetworkInfo getInfo() { + ConnectivityManager cm = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo current = cm.getActiveNetworkInfo(); + System.err.println("Current network info:"); + printInfo(current); + return current; + } + + private static void printInfo(NetworkInfo ni) { + if (ni == null) { + System.err.println("Network info is null"); + return; + } + System.err.println( + "state: " + ni.getState() + + " detail: " + ni.getDetailedState() + + " extrainfo: " + ni.getExtraInfo() + + " reason: " + ni.getReason() + + " typename: " + ni.getTypeName() + + " available: " + ni.isAvailable() + + " connected: " + ni.isConnected() + + " conorcon: " + ni.isConnectedOrConnecting() + + " failover: " + ni.isFailover()); + + } } diff --git a/src/net/i2p/android/router/service/Init.java b/src/net/i2p/android/router/service/Init.java index 772192bcb..baaa88841 100644 --- a/src/net/i2p/android/router/service/Init.java +++ b/src/net/i2p/android/router/service/Init.java @@ -75,7 +75,6 @@ class Init { void initialize() { mergeResourceToFile(R.raw.router_config, "router.config"); mergeResourceToFile(R.raw.logger_config, "logger.config"); - mergeResourceToFile(R.raw.clients_config, "clients.config"); mergeResourceToFile(R.raw.i2ptunnel_config, "i2ptunnel.config"); // FIXME this is a memory hog to merge this way mergeResourceToFile(R.raw.hosts_txt, "hosts.txt"); @@ -114,6 +113,8 @@ class Init { * Load defaults from resource, * then add props from file, * and write back + * For now, do it backwards so we can override with new apks. + * When we have user configurable stuff, switch it back. */ private void mergeResourceToFile(int resID, String f) { InputStream in = null; @@ -121,9 +122,10 @@ class Init { byte buf[] = new byte[4096]; try { - Properties props = new OrderedProperties(); in = ctx.getResources().openRawResource(resID); - DataHelper.loadProps(props, in); + Properties props = new OrderedProperties(); + // keep user settings + //DataHelper.loadProps(props, in); try { fin = ctx.openFileInput(f); @@ -131,10 +133,11 @@ class Init { System.err.println("Merging resource into file " + f); } catch (IOException ioe) { System.err.println("Creating file " + f + " from resource"); - } finally { - if (fin != null) try { fin.close(); } catch (IOException ioe) {} } + // override user settings + DataHelper.loadProps(props, in); + DataHelper.storeProps(props, ctx.getFileStreamPath(f)); } catch (IOException ioe) { } catch (Resources.NotFoundException nfe) { diff --git a/src/net/i2p/android/router/service/LoadClientsJob.java b/src/net/i2p/android/router/service/LoadClientsJob.java new file mode 100644 index 000000000..21180f4d1 --- /dev/null +++ b/src/net/i2p/android/router/service/LoadClientsJob.java @@ -0,0 +1,70 @@ +package net.i2p.android.router.service; + +import net.i2p.i2ptunnel.TunnelControllerGroup; +import net.i2p.router.Job; +import net.i2p.router.JobImpl; +import net.i2p.router.RouterContext; +import net.i2p.util.I2PThread; + +/** + * Load the clients we want. + * + * We can't use LoadClientAppsJob (reading in clients.config) directly + * because Class.forName() needs a PathClassLoader argument - + * http://doandroids.com/blogs/2010/6/10/android-classloader-dynamic-loading-of/ + * ClassLoader cl = new PathClassLoader(_apkPath, ClassLoader.getSystemClassLoader()); + * + * We can't extend LoadClientAppsJob to specify a class loader, + * even if we use it only for Class.forName() and not for + * setContextClassLoader(), because I2PTunnel still + * can't find the existing static RouterContext due to the new class loader. + * + * Also, if we load them that way, we can't register a shutdown hook. + * + * So fire off the ones we want here, without a clients.config file and + * without using Class.forName(). + * + */ +class LoadClientsJob extends JobImpl { + + /** this is the delay to load the clients. There are additional delays e.g. in i2ptunnel.config */ + private static final long LOAD_DELAY = 10*1000; + + + public LoadClientsJob(RouterContext ctx) { + super(ctx); + getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY); + } + + public String getName() { return "Start Clients"; }; + + public void runJob() { + Job j = new RunI2PTunnel(getContext()); + getContext().jobQueue().addJob(j); + // add other clients here + } + + private static class RunI2PTunnel extends JobImpl { + + public RunI2PTunnel(RouterContext ctx) { + super(ctx); + } + + public String getName() { return "Start I2P Tunnel"; }; + + public void runJob() { + System.err.println("Starting i2ptunnel"); + TunnelControllerGroup.main(null); + System.err.println("i2ptunnel started"); + getContext().addShutdownTask(new I2PTunnelShutdownHook()); + + } + } + + private static class I2PTunnelShutdownHook implements Runnable { + public void run() { + System.err.println("i2ptunnel shutdown hook"); + TunnelControllerGroup.getInstance().unloadControllers(); + } + } +} diff --git a/src/net/i2p/android/router/service/RouterService.java b/src/net/i2p/android/router/service/RouterService.java index 99f3cdb9d..c47e53762 100644 --- a/src/net/i2p/android/router/service/RouterService.java +++ b/src/net/i2p/android/router/service/RouterService.java @@ -1,18 +1,20 @@ package net.i2p.android.router.service; import android.app.Service; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; -import dalvik.system.PathClassLoader; - +import java.io.File; import java.text.DecimalFormat; import java.util.List; import net.i2p.android.router.R; +import net.i2p.android.router.receiver.I2PReceiver; import net.i2p.data.DataHelper; +import net.i2p.router.Job; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.RouterLaunch; @@ -26,11 +28,12 @@ public class RouterService extends Service { private RouterContext _context; private String _myDir; - private String _apkPath; + //private String _apkPath; private State _state = State.INIT; private Thread _starterThread; private Thread _statusThread; private StatusBar _statusBar; + private BroadcastReceiver _receiver; private final Object _stateLock = new Object(); private static final String MARKER = "************************************** "; @@ -44,7 +47,7 @@ public class RouterService extends Service { Init init = new Init(this); init.debugStuff(); init.initialize(); - _apkPath = init.getAPKPath(); + //_apkPath = init.getAPKPath(); _statusBar = new StatusBar(this); } @@ -59,11 +62,8 @@ public class RouterService extends Service { _state = State.STARTING; _starterThread = new Thread(new Starter()); - // this is required for Class.forName() to work in LoadClientAppsJob - // http://doandroids.com/blogs/2010/6/10/android-classloader-dynamic-loading-of/ - ClassLoader cl = new PathClassLoader(_apkPath, ClassLoader.getSystemClassLoader()); - _starterThread.setContextClassLoader(cl); _starterThread.start(); + _receiver = new I2PReceiver(this); } return START_STICKY; } @@ -86,6 +86,8 @@ public class RouterService extends Service { _statusBar.update("I2P is running"); _context = (RouterContext)contexts.get(0); _context.router().setKillVMOnEnd(false); + Job loadJob = new LoadClientsJob(_context); + _context.jobQueue().addJob(loadJob); _statusThread = new Thread(new StatusThread()); _statusThread.start(); _context.addShutdownTask(new ShutdownHook()); @@ -130,17 +132,19 @@ public class RouterService extends Service { fmt = new DecimalFormat("#0.00"); String status = + "I2P " + " Pr " + active + '/' + known + " Ex " + inEx + '/' + outEx + - " Cl " + inCl + '/' + outCl + + " Cl " + inCl + '/' + outCl; //" Pt " + part + - " BW " + fmt.format(inBW) + '/' + fmt.format(outBW) + "K" + + + String details = + "BW " + fmt.format(inBW) + '/' + fmt.format(outBW) + "K" + " Lg " + jobLag + " Dy " + msgDelay + " Up " + uptime; - System.out.println(status); - _statusBar.update(status); + _statusBar.update(status, details); } _statusBar.update("Status thread died"); System.err.println(MARKER + this + " status thread finished" + @@ -160,6 +164,14 @@ public class RouterService extends Service { public void onDestroy() { System.err.println("onDestroy called" + "Current state is: " + _state); + + BroadcastReceiver rcvr = _receiver; + if (rcvr != null) { + synchronized(rcvr) { + unregisterReceiver(rcvr); + _receiver = null; + } + } synchronized (_stateLock) { if (_state == State.STARTING) _starterThread.interrupt(); @@ -179,7 +191,8 @@ public class RouterService extends Service { public void run() { System.err.println(MARKER + this + " stopper thread" + "Current state is: " + _state); - _context.router().shutdown(Router.EXIT_HARD); + if (_context != null) + _context.router().shutdown(Router.EXIT_HARD); _statusBar.off(RouterService.this); System.err.println("shutdown complete"); synchronized (_stateLock) { @@ -192,12 +205,19 @@ public class RouterService extends Service { public void run() { System.err.println(this + " shutdown hook" + "Current state is: " + _state); + _statusBar.off(RouterService.this); + BroadcastReceiver rcvr = _receiver; + if (rcvr != null) { + synchronized(rcvr) { + unregisterReceiver(rcvr); + _receiver = null; + } + } synchronized (_stateLock) { if (_state == State.STARTING || _state == State.RUNNING) { _state = State.STOPPED; if (_statusThread != null) _statusThread.interrupt(); - _statusBar.off(RouterService.this); stopSelf(); } } diff --git a/src/net/i2p/android/router/service/StatusBar.java b/src/net/i2p/android/router/service/StatusBar.java index 08b473fd6..151f7b404 100644 --- a/src/net/i2p/android/router/service/StatusBar.java +++ b/src/net/i2p/android/router/service/StatusBar.java @@ -34,6 +34,10 @@ public class StatusBar { public void update(String details) { String title = "I2P Status"; + update(title, details); + } + + public void update(String title, String details) { PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); notif.setLatestEventInfo(ctx, title, details, pi); mgr.notify(ID, notif); -- GitLab