- Add Peers activity

- Save service state
- Start router after stopped by system
This commit is contained in:
zzz
2011-06-24 17:39:26 +00:00
parent 87cd67f498
commit a3aa64654f
8 changed files with 230 additions and 41 deletions

View File

@ -48,11 +48,17 @@
</activity>
<activity android:name=".activity.AddressbookActivity"
android:label="Address Book"
android.theme="@android:style/Theme.NoTitleBar" >
android.theme="@android:style/Theme.NoTitleBar"
android:launchMode="singleTop" >
</activity>
<activity android:name=".activity.LogActivity"
android:label="Logs"
android.theme="@android:style/Theme.NoTitleBar" >
</activity>
<activity android:name=".activity.PeersActivity"
android:label="Peers and Transport Status"
android.theme="@android:style/Theme.NoTitleBar"
android:launchMode="singleTop" >
</activity>
</application>
</manifest>

View File

@ -81,6 +81,12 @@
android:layout_height="wrap_content"
android:text="Error Logs"
/>
<Button
android:id="@+id/peers_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Peers"
/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"

20
res/layout/peers.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/peers_status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Peers"
/>
<WebView
android:id="@+id/peers_webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>

View File

@ -82,23 +82,6 @@ public abstract class I2PActivityBase extends Activity {
return edit.commit();
}
/**
* Start the service and bind to it
*/
protected boolean startRouter() {
Intent intent = new Intent();
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
System.err.println(this + " calling startService");
ComponentName name = startService(intent);
if (name == null)
System.err.println(this + " XXXXXXXXXXXXXXXXXXXX got from startService: " + name);
System.err.println(this + " got from startService: " + name);
boolean success = bindRouter(true);
if (!success)
System.err.println(this + " Bind router failed");
return success;
}
@Override
public void onResume()
{
@ -197,18 +180,38 @@ public abstract class I2PActivityBase extends Activity {
////// Service stuff
/**
* Start the service and bind to it
*/
protected boolean startRouter() {
Intent intent = new Intent();
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
System.err.println(this + " calling startService");
ComponentName name = startService(intent);
if (name == null)
System.err.println(this + " XXXXXXXXXXXXXXXXXXXX got from startService: " + name);
System.err.println(this + " got from startService: " + name);
boolean success = bindRouter(true);
if (!success)
System.err.println(this + " Bind router failed");
return success;
}
/**
* Bind only
*/
protected boolean bindRouter(boolean autoCreate) {
Intent intent = new Intent();
intent.setClassName(this, "net.i2p.android.router.service.RouterService");
System.err.println(this + " calling bindService");
_connection = new RouterConnection();
boolean success = bindService(intent, _connection, autoCreate ? BIND_AUTO_CREATE : 0);
System.err.println(this + " got from bindService: " + success);
System.err.println(this + " bindService: auto create? " + autoCreate + " success? " + success);
return success;
}
protected void unbindRouter() {
if (_isBound) {
if (_isBound && _connection != null) {
unbindService(_connection);
_routerService = null;
_isBound = false;
@ -220,8 +223,10 @@ public abstract class I2PActivityBase extends Activity {
public void onServiceConnected(ComponentName name, IBinder service) {
System.err.println(this + " connected to router service");
RouterBinder binder = (RouterBinder) service;
_routerService = binder.getService();
RouterService svc = binder.getService();
_routerService = svc;
_isBound = true;
onRouterBind(svc);
}
public void onServiceDisconnected(ComponentName name) {
@ -229,9 +234,16 @@ public abstract class I2PActivityBase extends Activity {
// save memory
_routerService = null;
_isBound = false;
onRouterUnbind();
}
}
/** callback from ServiceConnection, override as necessary */
protected void onRouterBind(RouterService svc) {}
/** callback from ServiceConnection, override as necessary */
protected void onRouterUnbind() {}
////// Router stuff
protected RouterContext getRouterContext() {

View File

@ -119,6 +119,14 @@ public class MainActivity extends I2PActivityBase {
}
});
b = (Button) findViewById(R.id.peers_button);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent intent = new Intent(view.getContext(), PeersActivity.class);
startActivity(intent);
}
});
b = (Button) findViewById(R.id.router_start_button);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
@ -297,6 +305,8 @@ public class MainActivity extends I2PActivityBase {
RouterService svc = _routerService;
String status =
"connected? " + Util.isConnected(this) +
"\nMemory: " + DataHelper.formatSize(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) +
"B / " + DataHelper.formatSize(Runtime.getRuntime().maxMemory()) + 'B' +
"\nhave ctx? " + (ctx != null) +
"\nhave svc? " + (svc != null) +
"\nis bound? " + _isBound +

View File

@ -0,0 +1,90 @@
package net.i2p.android.router.activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import java.io.StringWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import net.i2p.android.router.R;
import net.i2p.android.router.service.RouterService;
import net.i2p.router.CommSystemFacade;
public class PeersActivity extends I2PActivityBase {
private I2PWebViewClient _wvClient;
// TODO add some inline style
private static final String HEADER = "<html><head></head><body>";
private static final String FOOTER = "</body></html>";
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.peers);
WebView wv = (WebView) findViewById(R.id.peers_webview);
wv.getSettings().setLoadsImagesAutomatically(false);
// http://stackoverflow.com/questions/2369310/webview-double-tap-zoom-not-working-on-a-motorola-droid-a855
wv.getSettings().setUseWideViewPort(true);
_wvClient = new I2PWebViewClient();
wv.setWebViewClient(_wvClient);
wv.getSettings().setBuiltInZoomControls(true);
}
@Override
public void onResume() {
super.onResume();
update();
}
/**
* Not bound by the time onResume() is called, so we have to do it here.
* If it is bound we update twice.
*/
@Override
protected void onRouterBind(RouterService svc) {
update();
}
private void update() {
WebView wv = (WebView) findViewById(R.id.peers_webview);
CommSystemFacade comm = getCommSystem();
String data;
if (comm != null) {
StringWriter out = new StringWriter(32*1024);
out.append(HEADER);
try {
comm.renderStatusHTML(out, "http://thiswontwork.i2p/peers", 0);
out.append(FOOTER);
data = out.toString();
} catch (IOException ioe) {
data = HEADER + "Error: " + ioe + FOOTER;
}
} else {
data = HEADER + "No peer data available. The router is not running." + FOOTER;
}
try {
wv.loadData(data, "text/html", "UTF-8");
} catch (Exception e) {
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
WebView wv = (WebView) findViewById(R.id.peers_webview);
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
_wvClient.cancelAll();
wv.stopLoading();
if (wv.canGoBack()) {
wv.goBack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
}

View File

@ -4,6 +4,7 @@ import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@ -50,14 +51,20 @@ public class RouterService extends Service {
private Handler _handler;
private Runnable _updater;
private static final String SHARED_PREFS = "net.i2p.android.router";
private static final String LAST_STATE = "service.lastState";
private static final String EXTRA_RESTART = "restart";
private static final String MARKER = "************************************** ";
@Override
public void onCreate() {
State lastState = getSavedState();
setState(State.INIT);
System.err.println(this + " onCreate called" +
" Saved state is: " + lastState +
" Current state is: " + _state);
(new File(getFilesDir(), "wrapper.log")).delete();
//(new File(getFilesDir(), "wrapper.log")).delete();
_myDir = getFilesDir().getAbsolutePath();
Init init = new Init(this);
init.debugStuff();
@ -67,27 +74,41 @@ public class RouterService extends Service {
_binder = new RouterBinder(this);
_handler = new Handler();
_updater = new Updater();
if (lastState == State.RUNNING) {
Intent intent = new Intent(this, RouterService.class);
intent.putExtra(EXTRA_RESTART, true);
onStartCommand(intent, 12345, 67890);
}
}
/** NOT called by system if it restarts us after a crash */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.err.println(this + " onStart called" +
" Intent is: " + intent +
" Flags is: " + flags +
" ID is: " + startId +
" Current state is: " + _state);
boolean restart = intent != null && intent.getBooleanExtra(EXTRA_RESTART, false);
if (restart)
System.err.println(this + " RESTARTING");
synchronized (_stateLock) {
if (_state != State.INIT)
//return START_STICKY;
return START_NOT_STICKY;
_receiver = new I2PReceiver(this);
if (Util.isConnected(this)) {
_statusBar.update("I2P is starting up");
_state = State.STARTING;
if (restart)
_statusBar.update("I2P is restarting");
else
_statusBar.update("I2P is starting up");
setState(State.STARTING);
_starterThread = new Thread(new Starter());
_starterThread.start();
} else {
_statusBar.update("I2P is waiting for a network connection");
_state = State.WAITING;
_starterThread = new Thread(new Waiter());
_starterThread.start();
setState(State.WAITING);
_handler.postDelayed(new Waiter(), 10*1000);
}
}
_handler.removeCallbacks(_updater);
@ -108,7 +129,7 @@ public class RouterService extends Service {
if (_state != State.WAITING)
return;
_statusBar.update("Network connected, I2P is starting up");
_state = State.STARTING;
setState(State.STARTING);
_starterThread = new Thread(new Starter());
_starterThread.start();
}
@ -130,7 +151,7 @@ public class RouterService extends Service {
synchronized (_stateLock) {
if (_state != State.STARTING)
return;
_state = State.RUNNING;
setState(State.RUNNING);
List contexts = RouterContext.listContexts();
if ( (contexts == null) || (contexts.isEmpty()) )
throw new IllegalStateException("No contexts. This is usually because the router is either starting up or shutting down.");
@ -193,7 +214,7 @@ public class RouterService extends Service {
@Override
public IBinder onBind(Intent intent)
{
System.err.println("onBind called" +
System.err.println(this + "onBind called" +
" Current state is: " + _state);
return _binder;
}
@ -262,7 +283,7 @@ public class RouterService extends Service {
Thread stopperThread = new Thread(new Stopper(State.MANUAL_QUITTING, State.MANUAL_QUITTED));
stopperThread.start();
} else if (_state == State.WAITING) {
_state = State.MANUAL_QUITTING;
setState(State.MANUAL_QUITTING);
(new FinalShutdownHook()).run();
}
}
@ -287,7 +308,8 @@ public class RouterService extends Service {
}
public boolean canManualStart() {
return _state == State.MANUAL_STOPPED || _state == State.STOPPED;
// We can be in INIT if we restarted after crash but previous state was not RUNNING.
return _state == State.INIT || _state == State.MANUAL_STOPPED || _state == State.STOPPED;
}
public void manualStart() {
@ -297,7 +319,7 @@ public class RouterService extends Service {
if (!canManualStart())
return;
_statusBar.update("I2P is starting up");
_state = State.STARTING;
setState(State.STARTING);
_starterThread = new Thread(new Starter());
_starterThread.start();
}
@ -355,7 +377,7 @@ public class RouterService extends Service {
public Stopper(State next, State stop) {
nextState = next;
stopState = stop;
_state = next;
setState(next);
}
public void run() {
@ -368,7 +390,7 @@ public class RouterService extends Service {
System.err.println("********** Router shutdown complete");
synchronized (_stateLock) {
if (_state == nextState)
_state = stopState;
setState(stopState);
}
}
}
@ -401,7 +423,7 @@ public class RouterService extends Service {
_starterThread.interrupt();
if (_state == State.WAITING || _state == State.STARTING ||
_state == State.RUNNING)
_state = State.STOPPING;
setState(State.STOPPING);
}
}
}
@ -427,21 +449,44 @@ public class RouterService extends Service {
if (_state == State.STARTING)
_starterThread.interrupt();
if (_state == State.MANUAL_STOPPING) {
_state = State.MANUAL_STOPPED;
setState(State.MANUAL_STOPPED);
} else if (_state == State.NETWORK_STOPPING) {
// start waiter handler
_state = State.WAITING;
setState(State.WAITING);
_handler.postDelayed(new Waiter(), 10*1000);
} else if (_state == State.STARTING || _state == State.RUNNING ||
_state == State.STOPPING) {
System.err.println(this + " died of unknown causes");
_state = State.STOPPED;
setState(State.STOPPED);
stopSelf();
} else if (_state == State.MANUAL_QUITTING) {
_state = State.MANUAL_QUITTED;
setState(State.MANUAL_QUITTED);
stopSelf();
}
}
}
}
private State getSavedState() {
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS, 0);
String stateString = prefs.getString(LAST_STATE, State.INIT.toString());
for (State s : State.values()) {
if (s.toString().equals(stateString))
return s;
}
return State.INIT;
}
private void setState(State s) {
_state = s;
saveState();
}
/** @return success */
private boolean saveState() {
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS, 0);
SharedPreferences.Editor edit = prefs.edit();
edit.putString(LAST_STATE, _state.toString());
return edit.commit();
}
}

View File

@ -18,7 +18,7 @@ public abstract class Util {
String us = ctx.getPackageName();
try {
PackageInfo pi = pm.getPackageInfo(us, 0);
System.err.println("VersionCode" + ": " + pi.versionCode);
//System.err.println("VersionCode" + ": " + pi.versionCode);
// http://doandroids.com/blogs/2010/6/10/android-classloader-dynamic-loading-of/
//_apkPath = pm.getApplicationInfo(us, 0).sourceDir;
//System.err.println("APK Path" + ": " + _apkPath);