/*
* Copyright 2011-2012 Paddy Byers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.webinos.android.app.anode;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
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.platform.PlatformInit;
import org.webinos.android.util.ArgProcessor;
import org.webinos.android.util.Constants;
import org.webinos.android.util.ModuleUtils;
import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.util.Log;
public class AnodeService extends IntentService {
private static String TAG = "anode::AnodeService";
/**********************
* Instance table
***********************/
private static int counter;
private static HashMap<String, Isolate> instances = new HashMap<String, Isolate>();
public static synchronized String addInstance(String instance, Isolate isolate) {
if(instance == null) instance = String.valueOf(counter++);
instances.put(instance, isolate);
return instance;
}
static synchronized Isolate getInstance(String instance) {
return instances.get(instance);
}
static synchronized void removeInstance(String instance) {
instances.remove(instance);
}
static synchronized String soleInstance() {
String instance = null;
if(instances.size() == 1)
instance = instances.keySet().iterator().next();
return instance;
}
static synchronized Collection<Isolate> getAll() {
return instances.values();
}
/**********************
* Service
**********************/
public AnodeService() {
super(":anode.AnodeService");
/* android.os.Debug.waitForDebugger(); */
(new File(Constants.APP_DIR)).mkdirs();
(new File(Constants.MODULE_DIR)).mkdirs();
(new File(Constants.RESOURCE_DIR)).mkdirs();
(new File(Constants.WRT_DIR)).mkdirs();
}
private void initRuntime(String[] opts) {
try {
Runtime.initRuntime(this, opts);
} catch (InitialisationException e) {
Log.v(TAG, "AnodeService.initRuntime: exception: " + e + "; cause: " + e.getCause());
}
}
@Override
protected void onHandleIntent(Intent intent) {
/* we should not get a stop action; should have been intercepted by the receiver */
String action = intent.getAction();
if(AnodeReceiver.ACTION_STOP.equals(action)) {
Log.v(TAG, "AnodeService.onHandleIntent::stop: internal error");
return;
}
if(PlatformInit.ACTION_POSTINSTALL.equals(action)) {
if(Runtime.isInitialised()) {
/* the runtime is running .. we can't replace
* the modules while we're running. Therefore
* we have to kill the process and reschedule
* the intent to be handled by starting us
* again ....
*
* First kill all isolates */
for(Isolate isolate : AnodeService.getAll())
stopInstance(isolate);
/* post alarm event to re-wake us */
Log.v(TAG, "AnodeReceiver.onReceive::postinstall: service is running, so exit and reschedule");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, SystemClock.uptimeMillis() + 2000, pendingIntent);
/* kill this process */
System.exit(0);
return;
}
/* otherwise, we can just update the modules */
Log.v(TAG, "AnodeReceiver.onReceive::postinstall: starting PlatformInit service");
intent.setClass(this, PlatformInit.class);
startService(intent);
return;
}
if(AnodeReceiver.ACTION_START.equals(action)) {
/* get system options before handling this invocation */
String options = intent.getStringExtra(AnodeReceiver.OPTS);
String[] opts = options == null ? null : options.split("\\s");
initRuntime(opts);
handleStart(intent);
} else if(AnodeReceiver.ACTION_MODULE_INSTALL.equals(action)) {
handleInstall(intent);
} else if(AnodeReceiver.ACTION_MODULE_UNINSTALL.equals(action)) {
handleUninstall(intent);
}
}
private void handleStart(Intent intent) {
/* get the launch commandline */
String args = intent.getStringExtra(AnodeReceiver.CMD);
/* if no cmdline was sent, then launch the activity for interactive behaviour */
if(args == null || args.isEmpty()) {
intent.setClassName(getApplication(), AnodeActivity.class.getName());
getApplication().startActivity(intent);
return;
}
/* create a new instance based on the supplied args */
ArgProcessor argProcessor = new ArgProcessor(intent.getExtras(), args);
String[] processedArgs = argProcessor.processArray();
/* launch directly */
try {
Isolate isolate = Runtime.createIsolate();
String instance = intent.getStringExtra(AnodeReceiver.INST);
isolate.addStateListener(new ServiceListener(addInstance(instance, isolate)));
isolate.start(processedArgs);
} catch (IllegalStateException e) {
Log.v(TAG, "AnodeReceiver.onReceive::start: exception: " + e + "; cause: " + e.getCause());
} catch (NodeException e) {
Log.v(TAG, "AnodeReceiver.onReceive::start: exception: " + e + "; cause: " + e.getCause());
}
}
class ServiceListener implements StateListener {
String instance;
private ServiceListener(String instance) {
this.instance = instance;
}
@Override
public void stateChanged(final int state) {
/* exit remove the instance if exited */
if(state == Runtime.STATE_STOPPED) {
removeInstance(instance);
}
}
}
private void handleInstall(Intent intent) {
String module = intent.getStringExtra(AnodeReceiver.MODULE);
String path = intent.getStringExtra(AnodeReceiver.PATH);
ModuleUtils.install(this, module, path);
}
private void handleUninstall(Intent intent) {
ModuleUtils.uninstall(intent.getStringExtra(AnodeReceiver.MODULE));
}
static void stopInstance(Isolate isolate) {
try {
isolate.stop();
} catch (IllegalStateException e) {
Log.v(TAG, "AnodeReceiver.onReceive::stop: exception: " + e + "; cause: " + e.getCause());
} catch (NodeException e) {
Log.v(TAG, "AnodeReceiver.onReceive::stop: exception: " + e + "; cause: " + e.getCause());
}
}
}