/*
* Copyright (C) 2014 The Context Engine Project
*
* 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 uk.ac.tvu.mdse.contextengine;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import uk.ac.tvu.mdse.contextengine.PreferenceChangeComponent;
import uk.ac.tvu.mdse.contextengine.contexts.LocationContextTest;
import uk.ac.tvu.mdse.contextengine.db.ContextDB;
import uk.ac.tvu.mdse.contextengine.db.ContextDBImpl;
import uk.ac.tvu.mdse.contextengine.parser.ContextsParser;
import uk.ac.tvu.mdse.contextengine.parser.ParserHandler;
import uk.ac.tvu.mdse.contextengine.reasoning.ApplicationKey;
import uk.ac.tvu.mdse.contextengine.test.TestActivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;
import dalvik.system.DexClassLoader;
public class ContextEngineCore {
protected Context context;
protected static final String LOG_TAG = "ContextEngine";
protected static final boolean D = true;
// public String USER_CLASSPATH = null;
private final HashMap<String, Component> activeContexts = new HashMap<String, Component>();
// holding info about subscribed apps
private final ArrayList<ApplicationKey> applicationKeys = new ArrayList<ApplicationKey>();
// it is assumed that only one app define context
// composition at the time - it needs to gets synchronised later on!
ApplicationKey newAppKey;
boolean set = false;
// added because multiple messages received from one broadcast
String lastContextName = "";
String lastContextValue = "";
// private ArrayList<LocationContextTest> locationContexts = new
// ArrayList<LocationContextTest>();
LocationContextTest locationContext;
private LocationServices locationServices = null;
private SharedPreferences sp;
public static final String CONTEXT_INTENT = "uk.ac.tvu.mdse.contextengine.REMOTE_SERVICE";
public static final String CONTEXT_INFORMATION = "context_information";
public static final String CONTEXT_NAME = "context_name";
public static final String CONTEXT_DATE = "context_date";
public static final String CONTEXT_APPLICATION_KEY = "context_application_key";
// public static final String DEFAULT_CLASSPATH =
// "uk.ac.uwl.mdse.adspl.contextengine.contexts.";
protected BroadcastReceiver contextMonitor;
private ContextEngineService ces = null;
private ContextDB contextDB;
protected IntentFilter filter;
int mValue = 0;
// for notifications
static int count = 0;
static String order = "*";
private NotificationManager mNM;
public ContextEngineCore(Context c) {
context = c;
mNM = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
filter = new IntentFilter(
"uk.ac.tvu.mdse.contextengine.CONTEXT_CHANGED");
sp = PreferenceManager.getDefaultSharedPreferences(c);
contextDB = new ContextDBImpl(c);
setupContextMonitor();
// try{
// locationServices = new LocationServices(c);
// }catch(Exception e){
// if (D)
// Log.e(LOG_TAG, e.getLocalizedMessage());
// }
}
public List<String> getContextList(String appkey) {
return contextDB.getUsableContextList(appkey);
}
public String getContextValue(String appkey, String componentname) {
Component context = this.activeContexts.get(componentname);
if (context != null) {
return context.getContextInformation(appkey);
}
return "";
}
public boolean isComponentDeployed(String appkey, String componentname) {
List<String> component = contextDB.getLoadComponentInfo(appkey, componentname);
if (component != null)
return true;
else
return false;
}
public boolean newPreferenceComponent(String appKey, String preferenceName,
String preferenceType) {
PreferenceChangeComponent preferenceContext = new PreferenceChangeComponent(
sp, preferenceName, preferenceType, context);
activeContexts.put(preferenceName, preferenceContext);
Log.e(LOG_TAG, "newPreferenceComponent added");
return true;
}
// public boolean registerContextPathI(String path) {
// this.USER_CLASSPATH = path;
// return true;
// }
public boolean registerAppKey(String key) {
// if(newAppKey==null){
// newAppKey = new ApplicationKey(key);
// newAppKey.key = key;
// return true;
// }
// for(ApplicationKey k: applicationKeys){
// if(! (k.key.equalsIgnoreCase(key))){
// applicationKeys.add(newAppKey);
// newAppKey.key=key;
// return true;
// }
// }
// return false;
newAppKey = new ApplicationKey(key);
applicationKeys.add(newAppKey);
Log.d(LOG_TAG, "registerAppKey " + newAppKey.key);
return true;
}
public boolean newComponent(String appKey, String componentName) {
// FOR LOCATION:
// if (componentName.equals("LocationContext")) {
// if (locationServices == null)
// locationServices = new LocationServices(context);
// }
Component context = activeContexts.get(componentName);
if (context != null) {
Log.e(LOG_TAG, "Component already running!");
return false;
} else {
return loadClass(appKey, componentName);
}
}
private ApplicationKey getApplicationKey(String appKey) {
ApplicationKey appkey = null;
for (ApplicationKey ak : this.applicationKeys) {
if (ak.key.equalsIgnoreCase(appKey)) {
appkey = ak;
}
}
return appkey;
}
public boolean newContextValues(String appKey, String componentName,
String[] contextValues) {
Component context = activeContexts.get(componentName);
if (context == null) {
newComponent(appKey, componentName);
newContextValues(appKey, componentName, contextValues);
} else {
context.setupNewValuesSet(getApplicationKey(appKey), contextValues);
return true;
}
return false;
}
public boolean newContextValue(String appKey, String componentName,
String contextValue) {
try {
Component context = activeContexts.get(componentName);
if (context == null) {
newComponent(appKey, componentName);
newContextValue(appKey, componentName, contextValue);
} else {
// need to revisit newAppKey stuff...
context.addContextValue(newAppKey, contextValue);
}
return true;
} catch (Exception e) {
Log.e(LOG_TAG, e.getLocalizedMessage());
return false;
}
}
public void newSpecificContextValue(String appKey, String componentName,
String contextValue, String numericData1, String numericData2) {
if (D)
Log.d(LOG_TAG, "addSpecificContextValue");
try {
Component context = activeContexts.get(componentName);
if (context == null) {
newComponent(appKey, componentName);
newSpecificContextValue(appKey, componentName, contextValue,
numericData1, numericData2);
} else {
// need to revisit newAppKey stuff...
context.addSpecificContextValue(newAppKey, contextValue,
Double.valueOf(numericData1),
Double.valueOf(numericData2));
}
} catch (Exception e) {
Log.e(LOG_TAG, e.getLocalizedMessage());
}
}
public void removeContextValueSet(String appKey, String componentName) {
ApplicationKey key = null;
for (ApplicationKey k : this.applicationKeys) {
if (k.key.equalsIgnoreCase(appKey)) {
key = k;
}
}
// look up for the composite if created
Component context = activeContexts.get(componentName);
if ((context != null) && (key != null)) {
context.removeValuesSet(key);
if (context.getValuesSetCount() < 1) {
activeContexts.remove(componentName);
context.stop();
}
}
}
public void newRange(String appKey, String componentName, String minValue,
String maxValue, String contextValue) {
// look up for the component
Component context = activeContexts.get(componentName);
Log.d(LOG_TAG, "addRange" + componentName);
if (context == null) {
newComponent(appKey, componentName);
newRange(appKey, componentName, minValue, maxValue, contextValue);
} else {
context.addRange(newAppKey, Long.parseLong(minValue),
Long.parseLong(maxValue), contextValue);
}
}
public boolean addComposite(String compositeName) {
try {
CompositeComponent compositeContext = (CompositeComponent) activeContexts
.get(compositeName);
if (compositeContext != null) {
Log.e(LOG_TAG, "Component already running!");
// create new content values set
return false;
} else {
compositeContext = new CompositeComponent(compositeName,
context);
activeContexts.put(compositeName, compositeContext);
return true;
}
} catch (Exception e) {
Log.d(LOG_TAG, e.getLocalizedMessage());
return false;
}
}
public boolean addToCompositeM(String appKey, String componentName,
String compositeName) {
CompositeComponent compositeContext = (CompositeComponent) activeContexts
.get(compositeName);
Component context = activeContexts.get(componentName);
if (context == null) {
if (!(loadClass(appKey, componentName)))
return false;
}
if (compositeContext == null) {
compositeContext = new CompositeComponent(compositeName,
this.context);
activeContexts.put(compositeName, compositeContext);
}
compositeContext.registerComponent(context);
if (D)
Log.d(LOG_TAG, "Added" + componentName + " to " + compositeName);
return true;
}
public void newRule(String componentName, String[] condition, String result) {
CompositeComponent compositeContext = (CompositeComponent) activeContexts
.get(componentName);
if (compositeContext != null) {
compositeContext.addRule(condition, result);
Log.d(LOG_TAG, "addRule");
}
}
public void componentDefined(String name) {
Component context = activeContexts.get(name);
if (context != null) {
context.componentDefined(newAppKey);
}
}
public boolean compositeReady(String compositeName) {
CompositeComponent compositeContext = (CompositeComponent) activeContexts
.get(compositeName);
if (compositeContext != null) {
if (set == false) {
setupContextMonitor();
set = true;
}
compositeContext.componentDefined(newAppKey);
return true;
} else {
Log.e(LOG_TAG, "Composite not active!");
return false;
}
}
public void setupContextMonitor() {
Log.v("value", "add contextMonitor");
contextMonitor = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
"uk.ac.tvu.mdse.contextengine.CONTEXT_CHANGED")) {
if (D)
Log.d(LOG_TAG, "onReceive");
Bundle bundle = intent.getExtras();
// show notification - just for testing
String contextName = bundle
.getString(Component.CONTEXT_NAME);
String contextValue = bundle
.getString(Component.CONTEXT_INFORMATION);
// ArrayList<String> appKey = bundle
// .getStringArrayList(Component.CONTEXT_APPLICATION_KEY);
if (lastContextName.equals(contextName)
&& lastContextValue.equals(contextValue))
Log.v("value", "again the same msg");
else
// showNotification(contextName+ " "+contextValue);
sendBroadcastToApps(bundle);
}
}
};
context.registerReceiver(contextMonitor, filter);
}
protected void runXML(String path) {
try {
File file = new File(Environment.getExternalStorageDirectory()
+ path);
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
ParserHandler ph = new ParserHandler();
ph.setContextEngine(this);
xr.setContentHandler(ph);
xr.parse(new InputSource(new InputStreamReader(new FileInputStream(
file))));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
protected void runXMLParser(String path) {
try {
Log.d(LOG_TAG, "runWorkflowXML");
ContextsParser wcp = new ContextsParser();
wcp.readXMLfile(context, path, this);
} catch (Exception e) {
e.printStackTrace();
}
}
public void copyDexFile(String appKey, final String newDex,
String[] contexts, String packageName, int permission) {
File dexInternalStoragePath = new File(context.getDir("dex",
Context.MODE_PRIVATE), newDex);
// File newDexFile = new File(context.getExternalFilesDir(null),
// newDex);
String celoc = Environment.getExternalStorageDirectory()
+ "/Android/data/uk.ac.tvu.mdse.contextengine/files/";
File newDexFile = new File(celoc, newDex);
BufferedInputStream bis = null;
OutputStream dexWriter = null;
final int BUF_SIZE = 8 * 1024;
try {
bis = new BufferedInputStream(new FileInputStream(newDexFile));
dexWriter = new BufferedOutputStream(new FileOutputStream(
dexInternalStoragePath));
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
dexWriter.write(buf, 0, len);
}
dexWriter.close();
bis.close();
Log.v(LOG_TAG, "copied dex file");
for (String c : contexts) {
this.contextDB.insertComponent(packageName, c, appKey,
permission, newDex);
}
} catch (Exception e) {
Log.e("Error", e.getStackTrace().toString());
}
}
protected boolean loadClass(String appId, String componentName) {
List<String> componentInfo = this.contextDB.getLoadComponentInfo(appId,
componentName);
if (componentInfo.size() > 0) {
return loadClass(componentName, componentInfo.get(0),
componentInfo.get(1));
}
return false;
}
private boolean loadClass(String componentName, String dex,
String packagename) {
final File optimizedDexOutputPath = context.getDir("outdex",
Context.MODE_PRIVATE);
File dexInternalStoragePath;
// if (dex == null)
// dexInternalStoragePath = new File(context.getDir("dex",
// Context.MODE_PRIVATE),
// "classes.dex");
// else
dexInternalStoragePath = new File(context.getDir("dex",
Context.MODE_PRIVATE), dex);
DexClassLoader cl = new DexClassLoader(
dexInternalStoragePath.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(), null,
context.getClassLoader());
Class<?> contextClass = null;
Class<?>[] parameterTypes = { Context.class };
try {
// Load the Class
contextClass = cl
.loadClass(packagename.concat("." + componentName));
Constructor<?> contextConstructor = contextClass
.getConstructor(parameterTypes);
Component context = (Component) contextConstructor
.newInstance(this.context);
activeContexts.put(componentName, context);
return true;
} catch (ClassNotFoundException cnfe) {
Log.e(LOG_TAG, "Component does not exist!");
return false;
} catch (Exception e) {
// Log.e("Error", e.getStackTrace().toString());
e.printStackTrace();
return false;
}
}
// used originally to test broadcasting
// instead of sending message to app, the message is displayed as
// notification
@SuppressWarnings("deprecation")
private void showNotification(String contextChange) {
// In this sample, we'll use the same text for the ticker and the
// expanded notification
if (D)
Log.d(LOG_TAG, "showNotification");
CharSequence contentTitle = "ContextEngine";
CharSequence contentText = "Context Changed:" + contextChange;
long when = System.currentTimeMillis();
// this intent needs to be adapted, serves only for testing purposes!!!
Intent i;
i = new Intent(((ContextWrapper) context).getBaseContext(),
TestActivity.class);
// i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@SuppressWarnings("deprecation")
Notification notification = new Notification(R.drawable.stat_sample,
contentText, when);
PendingIntent contentIntent = PendingIntent.getActivity(ces, 0, i,
Intent.FLAG_ACTIVITY_NEW_TASK);
notification.setLatestEventInfo(ces, contentTitle, contentText,
contentIntent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// Send the notification.
// We use a layout id because it is a unique number. We use it later to
// cancel.
++count;
mNM.notify(count, notification);
}
public void sendBroadcastToApps(Bundle bundle) {
for (Component c : activeContexts.values()) {
if ((c.contextName.equals("NoOfReviewsUP"))
|| (c.contextName.equals("RatingUP")))
Log.d(LOG_TAG, c.contextName + ": 2");
else
Log.d(LOG_TAG, c.contextName + ": "
+ c.valuesSets.get(0).contextInformation);
}
Log.d(LOG_TAG, "broadcasting to apps" + bundle.getString(CONTEXT_NAME)
+ bundle.getString(CONTEXT_INFORMATION));
Intent intent = new Intent();
try {
intent.setAction(CONTEXT_INTENT);
intent.putExtra(CONTEXT_NAME, bundle.getString(CONTEXT_NAME));
intent.putExtra(CONTEXT_APPLICATION_KEY,
bundle.getString(CONTEXT_APPLICATION_KEY));
intent.putExtra(CONTEXT_DATE, bundle.getString(CONTEXT_DATE));
if (!bundle.getString(CONTEXT_INFORMATION).equals(null))
intent.putExtra(CONTEXT_INFORMATION,
bundle.getString(CONTEXT_INFORMATION));
// c.sendBroadcast(intent);
} catch (Exception e) {
Log.e("ContextEngine", "broadcasting to apps not working");
}
}
public void stop() {
Log.d(LOG_TAG, "onDestroy");
// Cancel the persistent notification.
mNM.cancel(R.string.local_service_started);
for (Component context : activeContexts.values()) {
context.stop();
context = null;
}
context.unregisterReceiver(contextMonitor);
if (locationServices != null) {
locationServices.stop();
locationServices = null;
}
this.contextDB.closeDB();
}
}