package edu.ucla.nesl.mca; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.*; import org.json.JSONException; import android.content.Context; import android.content.Intent; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Environment; import android.util.Log; import edu.mit.media.funf.IOUtils; import edu.mit.media.funf.configured.ConfiguredPipeline; import edu.mit.media.funf.configured.FunfConfig; import edu.mit.media.funf.probe.Probe; import edu.mit.media.funf.probe.builtin.AccelerometerSensorProbe; import edu.mit.media.funf.storage.BundleSerializer; import edu.ucla.nesl.mca.classifier.Classifier; import edu.ucla.nesl.mca.classifier.ClassifierBuilder; import edu.ucla.nesl.mca.classifier.DecisionTree; import edu.ucla.nesl.mca.feature.AlgorithmUtil; import edu.ucla.nesl.mca.feature.Feature; import edu.ucla.nesl.mca.feature.Feature.OPType; import edu.ucla.nesl.mca.feature.FeaturePool; import edu.ucla.nesl.mca.feature.SensorProfile; public class MainService extends ConfiguredPipeline { public static final String TAG = "MainService"; public static final String MAIN_CONFIG = "main_config"; public static final String ACTION_RUN_ONCE = "RUN_ONCE"; public static final String START_CLASSIFICATION = "START_CLASSIFICATION"; public static final String UPDATE_DATA = "updateData"; public static final String UPDATE_LOCATION = "updateLocation"; public static final String RUN_ONCE_PROBE_NAME = "PROBE_NAME"; public static final String DISPLAY_RESULT = "dispResult"; //private DecisionTree classifier; private static ArrayList<DecisionTree> classifiers = new ArrayList<DecisionTree>(); private static int currentClassifier = 0; private HashSet<Integer> featureManager; private ArrayList<TriggerEntry> featureTriggerOn; private ArrayList<TriggerEntry> featureTriggerOff; private ArrayList<TriggerEntry> resultTriggerOn; private ArrayList<TriggerEntry> resultTriggerOff; public static int count = 0; public static int windowSize = Integer.MAX_VALUE; private double[] accBufferX, accBufferY, accBufferZ; private long [] onTimeCount = new long[20]; private long [] offTimeCount = new long[20]; //private long [] offTimeCountClassifier = new long[20]; private long [] onTimeCountClassifier = new long[20]; LocationManager locationManager = null; class TriggerEntry { String type = ""; Feature triggerFeature = null; Feature triggeeFeature = null; int triggerClassifier = 0; Classifier triggeeClassifier = null; public String getType() { return type; } public void setType(String type) { this.type = type; } public Feature getTriggerFeature() { return triggerFeature; } public void setTriggerFeature(Feature triggerFeature) { this.triggerFeature = triggerFeature; } public Feature getTriggeeFeature() { return triggeeFeature; } public void setTriggeeFeature(Feature triggeeFeature) { this.triggeeFeature = triggeeFeature; } public int getTriggerClassifier() { return triggerClassifier; } public void setTriggerClassifier(int triggerClassifier) { this.triggerClassifier = triggerClassifier; } public Classifier getTriggeeClassifier() { return triggeeClassifier; } public void setTriggeeClassifier(Classifier triggeeClassifier) { this.triggeeClassifier = triggeeClassifier; } public TriggerEntry(String _type) { this.type = _type; } } LocationListener locationListener = new LocationListener() { public void onLocationChanged(Location location) { // Called when a new location is found by the network location // provider. double lat = location.getLatitude(); double logt = location.getLongitude(); double alt = location.getAltitude(); Log.i("MainService", "GPS!!!!! " + " latitute=" + lat + " logitute=" + logt + " altitute=" + alt); Intent intent2 = new Intent(UPDATE_LOCATION); intent2.setAction(UPDATE_LOCATION); intent2.putExtra("lat", lat); intent2.putExtra("lot", logt); sendBroadcast(intent2); } public void onStatusChanged(String provider, int status, Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } }; @Override protected void onHandleIntent(Intent intent) { Log.i("MainService", "Intent received"); if (intent.getAction().equals(START_CLASSIFICATION)) { // String probeName = intent.getStringExtra(RUN_ONCE_PROBE_NAME); // Log.i(TAG, probeName); // runProbeOnceNow(probeName); String fileName = intent.getExtras().getString("JSONFile"); featureManager = new HashSet<Integer>(); featureTriggerOn = new ArrayList<TriggerEntry>(); featureTriggerOff = new ArrayList<TriggerEntry>(); resultTriggerOn = new ArrayList<TriggerEntry>(); resultTriggerOff = new ArrayList<TriggerEntry>(); // Get the JSON input file File sdcard = Environment.getExternalStorageDirectory(); File file = new File(sdcard, fileName); try { /* Build the classifier */ ArrayList<Classifier> cls = ClassifierBuilder.BuildFromFile(file); for (Classifier cl : cls) { if (cl.getType().equals("TREE")) { classifiers.add((DecisionTree) cl); } } currentClassifier = 0; Log.i("MainService", "number of classifier" + classifiers.size()); Log.i("MainService", "classifier first feature: " + classifiers.get(currentClassifier).getInputs().get(1).getName()); Log.i("MainService", "classifier second feature: " + classifiers.get(currentClassifier).getInputs().get(2).getName()); Log.i("MainService", "second classifier first feature: " + classifiers.get(currentClassifier + 1).getRootFeature().getName()); /* * After building classifier, check each feature and run each * probe (start corresponding sensor manager) */ Log.i("MainService", "decision tree first node: " + classifiers.get(currentClassifier).getRootFeature().getName()); FeaturePool list = classifiers.get(currentClassifier).getInputs(); Log.i("MainService", "size=" + list.getM_index().size()); for (int index : list.getM_index()) { Feature feature = list.getFeature(index); int sensorID = feature.getSensor(); Log.i("MainService", "feature ID=" + feature.getId() + " sensor id=" + sensorID + " triggerOn?" + (feature.getTriggerOn() != null) + " triggerOff?" + (feature.getTriggerOff() != null)); if (!featureManager.contains(sensorID)) { if (feature.getTriggerOn() != null || feature.getTriggerOff() != null) { if (feature.getTriggerOn() != null) { int tOn = feature.getTriggerOn().getFeature(); Log.i("MainService", "tOn=" + tOn); if (tOn > 0) { Feature tFeature = null; for (int j : list.getM_index()) { if (list.getFeature(j).getId() == tOn) { tFeature = list.getFeature(j); } } TriggerEntry entry = new TriggerEntry("feature"); entry.setTriggerFeature(tFeature); entry.setTriggeeFeature(feature); featureTriggerOn.add(entry); Log.i("MainService", sensorID + " not started"); Log.i("MainService", tFeature.getName() + " can trigger " + feature.getName()); } else { TriggerEntry entry = new TriggerEntry("feature"); entry.setTriggerClassifier(Math.abs(tOn)); entry.setTriggeeFeature(feature); resultTriggerOn.add(entry); } } if (feature.getTriggerOff() != null) { int tOn = feature.getTriggerOff().getFeature(); if (tOn > 0) { Feature tFeature = null; for (int j : list.getM_index()) { if (list.getFeature(j).getId() == tOn) { tFeature = list.getFeature(j); } } TriggerEntry entry = new TriggerEntry("feature"); entry.setTriggerFeature(tFeature); entry.setTriggeeFeature(feature); featureTriggerOff.add(entry); Log.i("MainService", tFeature.getName() + " can trigger stopping " + feature.getName()); } else { TriggerEntry entry = new TriggerEntry("feature"); entry.setTriggerClassifier(Math.abs(tOn)); entry.setTriggeeFeature(feature); resultTriggerOff.add(entry); } } } else { if (feature.getWindowSize() <= windowSize) { windowSize = feature.getWindowSize(); } if (sensorID == SensorProfile.ACCELEROMETER) { featureManager.add(sensorID); Log.i("MainService", sensorID + " started"); accBufferX = new double[windowSize]; accBufferY = new double[windowSize]; accBufferZ = new double[windowSize]; runProbeOnceNow(AccelerometerSensorProbe.class.getName()); } else if (sensorID == SensorProfile.GPS) { } else { } } } } Log.i("MainService", "windowsize=" + windowSize); for (Classifier cl:classifiers) { if (cl.getTriggerOn() != null) { int tOn = cl.getTriggerOn().getFeature(); Log.i("MainService", "classifier trigger on ton=" + tOn); if (tOn > 0) { Feature tFeature = null; for (int j : list.getM_index()) { if (list.getFeature(j).getId() == tOn) { tFeature = list.getFeature(j); } } TriggerEntry entry = new TriggerEntry("classifier"); entry.setTriggerFeature(tFeature); entry.setTriggeeClassifier(cl); featureTriggerOn.add(entry); } else { TriggerEntry entry = new TriggerEntry("classifier"); entry.setTriggerClassifier(Math.abs(tOn)); entry.setTriggeeClassifier(cl); resultTriggerOn.add(entry); } } if (cl.getTriggerOff() != null) { int tOn = cl.getTriggerOff().getFeature(); if (tOn > 0) { Feature tFeature = null; for (int j : list.getM_index()) { if (list.getFeature(j).getId() == tOn) { tFeature = list.getFeature(j); } } TriggerEntry entry = new TriggerEntry("classifier"); entry.setTriggerFeature(tFeature); entry.setTriggeeClassifier(cl); featureTriggerOff.add(entry); } else { TriggerEntry entry = new TriggerEntry("classifier"); entry.setTriggerClassifier(Math.abs(tOn)); entry.setTriggeeClassifier(cl); resultTriggerOff.add(entry); } } } } catch (IOException e) { e.printStackTrace(); } } else { super.onHandleIntent(intent); } } @Override public BundleSerializer getBundleSerializer() { return null; } @Override public void onDataReceived(Bundle data) { // super.onDataReceived(data); Log.i("MainService", "data received"); // Toast.makeText(this, "data received!", Toast.LENGTH_SHORT).show(); float[] dx = data.getFloatArray("X"); float[] dy = data.getFloatArray("Y"); float[] dz = data.getFloatArray("Z"); Intent intent1 = new Intent(UPDATE_DATA); intent1.setAction(UPDATE_DATA); intent1.putExtra("x", Float.valueOf(dx[0]).toString()); intent1.putExtra("y", Float.valueOf(dy[0]).toString()); intent1.putExtra("z", Float.valueOf(dz[0]).toString()); sendBroadcast(intent1); // Log.i("MainService", "Acc data: x=" + dx[0] +" y=" + dy[0] + " z=" + // dz[0]); for (int i = 0; i < dx.length; i++) { accBufferX[count] = dx[i]; accBufferY[count] = dy[i]; accBufferZ[count] = dz[i]; count++; double var = 0.0; if (count == windowSize) { /* Start the evaluation of the classifier */ Bundle resData = new Bundle(); resData.putDoubleArray("AccX", accBufferX); resData.putDoubleArray("AccY", accBufferY); resData.putDoubleArray("AccZ", accBufferZ); FeaturePool list = classifiers.get(currentClassifier).getInputs(); for (int index : list.getM_index()) { Feature feature = list.getFeature(index); if (feature.getSensor() == SensorProfile.ACCELEROMETER) { feature.setData(resData); } } /* See if we need to trigger some sensor */ for (TriggerEntry entry:featureTriggerOn) { Feature triggee = entry.getTriggeeFeature(); Feature trigger = entry.getTriggerFeature(); Object obj = trigger.evaluate(0); if (obj instanceof Double) { var = (Double) obj; } if(triggee.getTriggerOn() != null) { // Log.i("MainService", "Current trigger value: " + var); if(triggee.getTriggerOn().getType() == OPType.REAL) { if (triggee.getTriggerOn().getRealOp().evaluate(var, triggee.getTriggerOn().getThreshold())) { if (triggee.getTriggerOn().getDuration() == 0.0 || onTimeCount[triggee.getId()] == triggee.getTriggerOn().getDuration()) { if (!featureManager.contains(triggee.getSensor())) { featureManager.add(triggee.getSensor()); /* Does it satisfy the duration requirement ?? */ onTimeCount[triggee.getId()] = 0; if (triggee.getSensor() == SensorProfile.GPS) { Log.i("MainService", "Current trigger value: " + var); locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); Log.i("MainService", "Start GPS"); Intent intent4 = new Intent(UPDATE_LOCATION); intent4.setAction(UPDATE_LOCATION); intent4.putExtra("lat", "No Signal"); intent4.putExtra("lot", "No Signal"); sendBroadcast(intent4); } else if (triggee.getSensor() == SensorProfile.ACCELEROMETER) { } else { } } } else { onTimeCount[triggee.getId()]++; Log.i("MainService", "current count " + onTimeCount[triggee.getId()]); } } else { onTimeCount[triggee.getId()] = 0; } } else if(triggee.getTriggerOn().getType() == OPType.NOMINAL) { } } /* Trigger off part, still need to rewrite */ // if (triggee.getTriggerOff() != null) { // // Log.i("MainService", "Current trigger value: " + var); // if(triggee.getTriggerOff().getType() == OPType.REAL) { // if (triggee.getTriggerOff().getRealOp().evaluate(var, triggee.getTriggerOff().getThreshold())) { // if (featureManager.contains(triggee.getSensor())) { // featureManager.remove(triggee.getSensor()); // if (triggee.getSensor() == SensorProfile.GPS) { // Log.i("MainService", "Current STOP trigger value: " + var); // locationManager.removeUpdates(locationListener); // } // else if (triggee.getSensor() == SensorProfile.ACCELEROMETER) { // // } // else { // // } // } // } // } // else if(triggee.getTriggerOn().getType() == OPType.NOMINAL) { // if (triggee.getTriggerOff() != null && triggee.getTriggerOff().getDuration() == 0.0) { // // } // if (triggee.getTriggerOff() != null && triggee.getTriggerOff().getDuration() == 0.0) { // // } // } // } } Log.i("MainService", "To log: current classifier=" + currentClassifier); Object result = classifiers.get(currentClassifier).evaluate(); /* If the result can trigger to start/stop some sensors */ for (TriggerEntry entry:resultTriggerOn) { Log.i("MainService", entry.getTriggerClassifier() + " " + currentClassifier); if (entry.getType().equals("classifier") && entry.getTriggerClassifier() == (currentClassifier + 1)) { Classifier triggee = entry.getTriggeeClassifier(); if(triggee.getTriggerOn() != null) { if(triggee.getTriggerOn().getType() == OPType.NOMINAL) { if(triggee.getTriggerOn().getValue().equals(result.toString())) { if (triggee.getTriggerOn().getDuration() == 0.0 || onTimeCountClassifier[triggee.getId()] == triggee.getTriggerOn().getDuration()) { if (currentClassifier != (triggee.getId() - 1)) { currentClassifier = (triggee.getId() - 1); onTimeCountClassifier[triggee.getId()] = 0; } } else { onTimeCountClassifier[triggee.getId()]++; Log.i("MainService", "current count " + result.toString() + " for off" + onTimeCountClassifier[triggee.getId()]); } } else { onTimeCountClassifier[triggee.getId()] = 0; } } } } if (entry.getType().equals("feature")) { Feature triggee = entry.getTriggeeFeature(); if(triggee.getTriggerOn().getType() == OPType.NOMINAL) { if(triggee.getTriggerOn().getValue().equals(result.toString())) { if (triggee.getTriggerOn().getDuration() == 0.0 || onTimeCount[triggee.getId()] == triggee.getTriggerOn().getDuration()) { if (!featureManager.contains(triggee.getSensor())) { featureManager.add(triggee.getSensor()); /* Does it satisfy the duration requirement ?? */ onTimeCount[triggee.getId()] = 0; if (triggee.getSensor() == SensorProfile.GPS) { AlgorithmUtil.setOutdoor(); Log.i("MainService", "Current trigger value: " + var); locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); Log.i("MainService", "Start GPS"); Intent intent4 = new Intent(UPDATE_LOCATION); intent4.setAction(UPDATE_LOCATION); intent4.putExtra("lat", "No Signal"); intent4.putExtra("lot", "No Signal"); sendBroadcast(intent4); } } } else { onTimeCount[triggee.getId()]++; Log.i("MainService", "current " + result.toString() + " count for off" + onTimeCount[triggee.getId()]); } } else { onTimeCount[triggee.getId()] = 0; } } } } for (TriggerEntry entry:resultTriggerOff) { if (entry.getType().equals("classifier") && entry.getTriggerClassifier() == currentClassifier) { } if (entry.getType().equals("feature")) { Feature triggee = entry.getTriggeeFeature(); if(triggee.getTriggerOff() != null) { if(triggee.getTriggerOff().getType() == OPType.REAL) { if (triggee.getTriggerOff().getRealOp().evaluate(var, triggee.getTriggerOff().getThreshold())) { if (triggee.getTriggerOff().getDuration() == 0.0 || offTimeCount[triggee.getId()] == triggee.getTriggerOff().getDuration()) { if (featureManager.contains(triggee.getSensor())) { featureManager.remove(triggee.getSensor()); /* Does it satisfy the duration requirement ?? */ onTimeCount[triggee.getId()] = 0; if (triggee.getSensor() == SensorProfile.GPS) { locationManager.removeUpdates(locationListener); AlgorithmUtil.setIndoor(); Log.i("MainService", "Set indoor to be true"); } else if (triggee.getSensor() == SensorProfile.ACCELEROMETER) { } else { } } } else { offTimeCount[triggee.getId()]++; } } else { offTimeCount[triggee.getId()] = 0; } } else if(triggee.getTriggerOff().getType() == OPType.NOMINAL) { if(triggee.getTriggerOff().getValue().equals(result.toString())) { if (triggee.getTriggerOff().getDuration() == 0.0 || offTimeCount[triggee.getId()] == triggee.getTriggerOff().getDuration()) { if (featureManager.contains(triggee.getSensor())) { featureManager.remove(triggee.getSensor()); /* Does it satisfy the duration requirement ?? */ onTimeCount[triggee.getId()] = 0; if (triggee.getSensor() == SensorProfile.GPS) { locationManager.removeUpdates(locationListener); AlgorithmUtil.setIndoor(); Log.i("MainService", "Set indoor to be true"); Intent intent3 = new Intent(UPDATE_LOCATION); intent3.setAction(UPDATE_LOCATION); intent3.putExtra("lat", "None"); intent3.putExtra("lot", "None"); sendBroadcast(intent3); } else if (triggee.getSensor() == SensorProfile.ACCELEROMETER) { } else { } } } else { offTimeCount[triggee.getId()]++; Log.i("MainService", "current " + result.toString() + " count for off" + offTimeCount[triggee.getId()]); } } else { offTimeCount[triggee.getId()] = 0; } } } } } Intent intent = new Intent(DISPLAY_RESULT); intent.setAction(DISPLAY_RESULT); intent.putExtra("mode", result.toString()); intent.putExtra("indoor", AlgorithmUtil.getOutdoor() ? "Outdoor" : "Indoor"); if (featureManager.contains(SensorProfile.GPS)) { intent.putExtra("gps", "GPS On"); } else { intent.putExtra("gps", "GPS Off"); } sendBroadcast(intent); count = 0; Arrays.fill(accBufferX, 0); Arrays.fill(accBufferY, 0); Arrays.fill(accBufferZ, 0); } } } @Override public void onStatusReceived(Probe.Status status) { super.onStatusReceived(status); // Fill this in with extra behaviors on status received } @Override public void onDetailsReceived(Probe.Details details) { super.onDetailsReceived(details); // Fill this in with extra behaviors on details received } @Override public FunfConfig getConfig() { return getMainConfig(this); } /** * Easy access to Funf config. As long as this service is running, changes * will be automatically picked up. * * @param context * @return */ public static FunfConfig getMainConfig(Context context) { FunfConfig config = getConfig(context, MAIN_CONFIG); if (config.getName() == null) { String jsonString = getStringFromAsset(context, "default_config.json"); //Toast.makeText(context, jsonString, Toast.LENGTH_SHORT).show(); if (jsonString == null) { Log.e(TAG, "Error loading default config. Using blank config."); jsonString = "{}"; } try { config.edit().setAll(jsonString).commit(); } catch (JSONException e) { Log.e(TAG, "Error parsing default config", e); } } return config; } public static String getStringFromAsset(Context context, String filename) { InputStream is = null; try { is = context.getAssets().open(filename); return IOUtils.inputStreamToString(is, Charset.defaultCharset() .name()); } catch (IOException e) { Log.e(TAG, "Unable to read asset to string", e); return null; } finally { if (is != null) { try { is.close(); } catch (IOException e) { Log.e(TAG, "Unable to close asset input stream", e); } } } } public void runProbeOnceNow(final String probeName) { FunfConfig config = getMainConfig(this); ArrayList<Bundle> updatedRequests = new ArrayList<Bundle>(); Bundle[] existingRequests = config.getDataRequests(probeName); if (existingRequests != null) { for (Bundle existingRequest : existingRequests) { updatedRequests.add(existingRequest); } } Bundle oneTimeRequest = new Bundle(); oneTimeRequest.putLong(Probe.Parameter.Builtin.PERIOD.name, 0L); updatedRequests.add(oneTimeRequest); Intent request = new Intent(Probe.ACTION_REQUEST); request.setClassName(this, probeName); request.putExtra(Probe.CALLBACK_KEY, getCallback()); request.putExtra(Probe.REQUESTS_KEY, updatedRequests); startService(request); } }