package org.webinos.android.app.pzp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import org.meshpoint.anode.Isolate;
import org.meshpoint.anode.Runtime;
import org.meshpoint.anode.Runtime.IllegalStateException;
import org.meshpoint.anode.Runtime.InitialisationException;
import org.meshpoint.anode.Runtime.NodeException;
import org.meshpoint.anode.Runtime.StateListener;
import org.webinos.android.app.anode.AnodeService;
import org.webinos.android.app.platform.Config;
import org.webinos.android.app.platform.PlatformInit;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class PzpService extends Service implements StateListener {
private static final String TAG = PzpService.class.getCanonicalName();
/**
* Listener for asynchronous indications of service availability
*/
interface PzpServiceListener {
public void onServiceAvailable(PzpService service);
}
public enum PzpState {
STATE_UNINITIALISED,
STATE_CREATED,
STATE_STARTED,
STATE_STOPPING,
STATE_STOPPED
}
/**
* Listener for asynchronous indications of service availability
*/
interface PzpStateListener {
public void onStateChanged(PzpState state);
}
/**
* Listeners waiting for service availability
*/
private static List<PzpServiceListener> serviceListeners = new ArrayList<PzpServiceListener>();
/**
* The singleton service
*/
private static PzpService theService;
/**
* Synchronously obtain the service instance, if it already exists.
* This will not trigger the service being started; only use in contexts
* where it is known that the service will already have been started.
* @return the service instance if available
*/
static synchronized PzpService getService() { return theService; }
/**
* Get the service instance, registering a callback for the case that
* the service is not currently available. If not available, the service
* will be started.
* @param ctx a context to use to start the service if required.
* @param listener a listener to call with the service instance, in the case that
* the service was not available already. If the service was available at the time
* the call was made, then the instance will be returned directly and the listener
* will NOT be called.
* @return the service instance if available; or null otherwise
*/
public static void getService(Context ctx, PzpServiceListener listener) {
PzpService foundService = null;
/* synchronously check, and add listener if necessary */
synchronized(PzpService.class) {
if(theService == null && listener != null) {
serviceListeners.add(listener);
}
foundService = theService;
}
/* start service if necessary */
if(foundService == null) {
ctx.startService(new Intent(ctx, PzpService.class));
} else if(listener != null) {
listener.onServiceAvailable(foundService);
}
}
/*******************
* private state
*******************/
private List<PzpStateListener> stateListeners = new ArrayList<PzpStateListener>();
private ConfigParams configParams;
private String instance;
private Isolate isolate;
private PzpState state = PzpState.STATE_UNINITIALISED;
private static final String LASTCONFIG = "lastconfig";
private static final String LOGCONFIG = "logconfig";
/*******************
* service prerequisites
*******************/
@Override
public void onCreate() {
super.onCreate();
/* if the platform is not yet initialised, wait until that has
* completed and retry */
if(PlatformInit.onInit(this, new Runnable() {
@Override
public void run() {
/* init (deferred case) */
onPlatformInit();
}
})) {
/* init (no wait) */
onPlatformInit();
}
}
private void onPlatformInit() {
/* init config */
initConfig();
/* synchronously set ourselves as the singleton instance, and notify
* any pending listeners */
synchronized(PzpService.class) {
theService = this;
for(PzpServiceListener listener : serviceListeners)
listener.onServiceAvailable(theService);
serviceListeners = null;
}
if("true".equals(getConfig().autoStart))
startPzp();
//FIXME: if autoStart is set to false, init progress will not terminate, deadlock
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
/*******************
* Config
*******************/
public class ConfigParams {
String autoStart;
String verboseLogging;
void readConfig() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(openFileInput(LASTCONFIG)));
autoStart = reader.readLine();
} catch (IOException e) {
} finally {
try {
if(reader != null)
reader.close();
} catch (IOException e) {}
}
try {
reader = new BufferedReader(new InputStreamReader(openFileInput(LOGCONFIG)));
verboseLogging = reader.readLine();
} catch (IOException e) {
} finally {
try {
if(reader != null)
reader.close();
} catch (IOException e) {}
}
}
void writeConfig() {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(openFileOutput(LASTCONFIG, MODE_PRIVATE)));
writer.write(autoStart + '\n');
} catch (IOException e) {
} finally {
try {
if(writer != null)
writer.close();
} catch (IOException e) {}
}
try {
writer = new BufferedWriter(new OutputStreamWriter(openFileOutput(LOGCONFIG, MODE_PRIVATE)));
writer.write(verboseLogging + '\n');
} catch (IOException e) {
} finally {
try {
if(writer != null)
writer.close();
} catch (IOException e) {}
}
}
String getCmd() {
return Config.getInstance().getProperty("pzp.cmd");
}
}
private void initConfig() {
/* read last config */
configParams = new ConfigParams();
configParams.readConfig();
/* set defaults if not already set */
Config config = Config.getInstance();
if(configParams.autoStart == null)
configParams.autoStart = config.getProperty("pzp.autoStart");
if(configParams.verboseLogging == null)
configParams.verboseLogging = config.getProperty("pzp.verboseLogging");
}
/*******************
* public API
*******************/
public ConfigParams getConfig() {
return configParams;
}
public void updateConfig() {
configParams.writeConfig();
}
public void startPzp() {
String cmd = configParams.getCmd() + ("true".equals(configParams.verboseLogging)?" --debug":"");
Log.v(TAG, "PZP start: starting with cmd: " + cmd);
try {
Runtime.initRuntime(this, new String[]{});
isolate = Runtime.createIsolate();
isolate.addStateListener(this);
this.instance = AnodeService.addInstance(instance, isolate);
isolate.start(cmd.split("\\s"));
sendBroadcast(new Intent("org.webinos.android.app.wrt.ui.PROGRESS"));
} catch (IllegalStateException e) {
Log.v(TAG, "isolate start: exception: " + e + "; cause: " + e.getCause());
} catch (NodeException e) {
Log.v(TAG, "isolate start: exception: " + e);
} catch (InitialisationException e) {
Log.v(TAG, "runtime init: exception: " + e);
}
}
public void stopPzp() {
if(instance == null) {
Log.v(TAG, "AnodeReceiver.onReceive::stop: no instance currently running for this activity");
return;
}
try {
isolate.stop();
} catch (IllegalStateException e) {
Log.v(TAG, "isolate stop : exception: " + e + "; cause: " + e.getCause());
} catch (NodeException e) {
Log.v(TAG, "isolate stop: exception: " + e);
}
}
/*******************
* PZP state management
*******************/
@Override
public void stateChanged(int anodeState) {
PzpStateListener[] iterator;
PzpState updatedState = PzpState.STATE_UNINITIALISED;
/* convert the isolate state to a PzpState */
switch(anodeState) {
case Runtime.STATE_CREATED:
updatedState = PzpState.STATE_CREATED;
break;
case Runtime.STATE_STARTED:
updatedState = PzpState.STATE_STARTED;
break;
case Runtime.STATE_STOPPING:
updatedState = PzpState.STATE_STOPPING;
break;
case Runtime.STATE_STOPPED:
updatedState = PzpState.STATE_STOPPED;
break;
}
/* synchronously update the state, and get a snapshot of the current
* listeners */
synchronized(this) {
state = updatedState;
iterator = stateListeners.toArray(new PzpStateListener[stateListeners.size()]);
}
/* notify listeners */
for(PzpStateListener listener : iterator)
listener.onStateChanged(updatedState);
}
/**
* Get the current PZP state
* @return the current state
*/
public synchronized PzpState getPzpState() {
return state;
}
/**
* Add a listener for PZP state changes
* @param listener the listener
*/
public synchronized void addPzpStateListener(PzpStateListener listener) {
stateListeners.add(listener);
}
/**
* Remove an existing PZP state listener
* @param listener the listener
*/
public synchronized void removePzpStateListener(PzpStateListener listener) {
stateListeners.remove(listener);
}
}