package com.mobilyzer; import java.io.InvalidClassException; import java.util.Arrays; import java.util.HashMap; import java.util.Set; import java.util.concurrent.Callable; import com.mobilyzer.exceptions.MeasurementError; import com.mobilyzer.measurements.DnsLookupTask; import com.mobilyzer.measurements.HttpTask; import com.mobilyzer.measurements.PageLoadTimeTask; import com.mobilyzer.measurements.PingTask; import com.mobilyzer.measurements.RRCTask; import com.mobilyzer.measurements.SequentialTask; import com.mobilyzer.measurements.TCPThroughputTask; import com.mobilyzer.measurements.TracerouteTask; import com.mobilyzer.measurements.UDPBurstTask; import com.mobilyzer.measurements.VideoQoETask; import android.os.Parcel; import android.os.Parcelable; public abstract class MeasurementTask implements Callable<MeasurementResult[]>, Comparable, Parcelable { protected MeasurementDesc measurementDesc; protected String taskId; public static final int USER_PRIORITY = Integer.MIN_VALUE; /* used for Server tasks */ public static final int INVALID_PRIORITY = Integer.MAX_VALUE; public static final int GCM_PRIORITY = 1234;//TODO just for testing public static final int INFINITE_COUNT = -1; private static HashMap<String, Class> measurementTypes; // Maps between the type of task and its readable name private static HashMap<String, String> measurementDescToType; static { measurementTypes = new HashMap<String, Class>(); measurementDescToType = new HashMap<String, String>(); measurementTypes.put(PingTask.TYPE, PingTask.class); measurementDescToType.put(PingTask.DESCRIPTOR, PingTask.TYPE); measurementTypes.put(HttpTask.TYPE, HttpTask.class); measurementDescToType.put(HttpTask.DESCRIPTOR, HttpTask.TYPE); measurementTypes.put(TracerouteTask.TYPE, TracerouteTask.class); measurementDescToType.put(TracerouteTask.DESCRIPTOR, TracerouteTask.TYPE); measurementTypes.put(DnsLookupTask.TYPE, DnsLookupTask.class); measurementDescToType.put(DnsLookupTask.DESCRIPTOR, DnsLookupTask.TYPE); measurementTypes.put(TCPThroughputTask.TYPE, TCPThroughputTask.class); measurementDescToType.put(TCPThroughputTask.DESCRIPTOR, TCPThroughputTask.TYPE); measurementTypes.put(UDPBurstTask.TYPE, UDPBurstTask.class); measurementDescToType.put(UDPBurstTask.DESCRIPTOR, UDPBurstTask.TYPE); measurementTypes.put(RRCTask.TYPE, RRCTask.class); measurementTypes.put(PageLoadTimeTask.TYPE, PageLoadTimeTask.class); measurementTypes.put(SequentialTask.TYPE, SequentialTask.class); // measurementDescToType.put(PageLoadTimeTask.DESCRIPTOR, PageLoadTimeTask.TYPE); measurementTypes.put(VideoQoETask.TYPE, VideoQoETask.class); // Hongyi: RRCTask is not accessible by users. So we don't put RRC descriptor // and type into this map // measurementDescToType.put(RRCTask.DESCRIPTOR, RRCTask.TYPE); } /** * @param measurementDesc * @param parent */ protected MeasurementTask(MeasurementDesc measurementDesc) { super(); this.measurementDesc = measurementDesc; generateTaskID(); } /* Compare priority as the first order. Then compare start time. */ @Override public int compareTo(Object t) { MeasurementTask another = (MeasurementTask) t; if (this.measurementDesc.startTime != null && another.measurementDesc.startTime != null) { return this.measurementDesc.startTime.compareTo( another.measurementDesc.startTime); } return 0; } public long timeFromExecution() { return this.measurementDesc.startTime.getTime() - System.currentTimeMillis(); } public boolean isPassedDeadline() { if (this.measurementDesc.endTime == null) { return false; } else { long endTime = this.measurementDesc.endTime.getTime(); return endTime <= System.currentTimeMillis(); } } public String getMeasurementType() { return this.measurementDesc.type; } public String getKey() { return this.measurementDesc.key; } public void setKey(String key) { this.measurementDesc.key = key; } public MeasurementDesc getDescription() { return this.measurementDesc; } /** Gets the currently available measurement descriptions */ public static Set<String> getMeasurementNames() { return measurementDescToType.keySet(); } /** Gets the currently available measurement types */ public static Set<String> getMeasurementTypes() { return measurementTypes.keySet(); } /** * Get the type of a measurement based on its name. Type is for JSON interface only where as * measurement name is a readable string for the UI */ public static String getTypeForMeasurementName(String name) { return measurementDescToType.get(name); } public static Class getTaskClassForMeasurement(String type) { return measurementTypes.get(type); } /* * This is put here for consistency that all MeasurementTask should have a * getDescClassForMeasurement() method. However, the MeasurementDesc is abstract and cannot be * instantiated */ public static Class getDescClass() throws InvalidClassException { throw new InvalidClassException("getDescClass() should only be invoked on " + "subclasses of MeasurementTask."); } public String getTaskId() { return taskId; } /** * Returns a brief human-readable descriptor of the task. */ public abstract String getDescriptor(); @Override public abstract MeasurementResult[] call() throws MeasurementError; /** Return the string indicating the measurement type. */ public abstract String getType(); @Override public abstract MeasurementTask clone(); /** * Stop the measurement, even when it is running. There is no side effect if the measurement has * not started or is already finished. */ public abstract boolean stop(); public abstract long getDuration(); public abstract void setDuration(long newDuration); @Override public boolean equals(Object o) { MeasurementTask another = (MeasurementTask) o; if (this.getDescription().equals(another.getDescription()) && this.getType().equals(another.getType())) { return true; } return false; } @Override public int hashCode() { StringBuilder taskstrbld = new StringBuilder(getMeasurementType()); taskstrbld.append(",") .append(this.measurementDesc.key).append(",") .append(this.measurementDesc.startTime).append(",") .append(this.measurementDesc.endTime).append(",") .append(this.measurementDesc.intervalSec).append(",") .append(this.measurementDesc.priority); Object[] keys = this.measurementDesc.parameters.keySet().toArray(); Arrays.sort(keys); for (Object k : keys) { taskstrbld.append(",").append(this.measurementDesc.parameters.get((String) k)); } return taskstrbld.toString().hashCode(); } /** * return hashcode of MeasurementTask as taskId. */ public void generateTaskID() { taskId = this.hashCode() + ""; } protected MeasurementTask(Parcel in) { // ClassLoader loader = Thread.currentThread().getContextClassLoader(); measurementDesc = in.readParcelable(MeasurementDesc.class.getClassLoader()); taskId = in.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(measurementDesc, flags); dest.writeString(taskId); } /** * All measurement tasks must provide measurements of how much data they have * used to be fetched when the task completes. This allows us to make sure we * stay under the data limit. * * @return Data consumed, in bytes */ public abstract long getDataConsumed(); }